ksxt 1.0.4__py3-none-any.whl → 1.0.5__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 (77) hide show
  1. ksxt/__pycache__/__init__.cpython-312.pyc +0 -0
  2. ksxt/__pycache__/bithumb.cpython-312.pyc +0 -0
  3. ksxt/__pycache__/koreainvest.cpython-312.pyc +0 -0
  4. ksxt/__pycache__/upbit.cpython-312.pyc +0 -0
  5. ksxt/api/__pycache__/__init__.cpython-312.pyc +0 -0
  6. ksxt/api/__pycache__/bithumb.cpython-312.pyc +0 -0
  7. ksxt/api/__pycache__/koreainvest.cpython-312.pyc +0 -0
  8. ksxt/api/__pycache__/upbit.cpython-312.pyc +0 -0
  9. ksxt/api/auto/__pycache__/bithumb.cpython-312.pyc +0 -0
  10. ksxt/api/auto/__pycache__/koreainvest.cpython-312.pyc +0 -0
  11. ksxt/api/auto/__pycache__/upbit.cpython-312.pyc +0 -0
  12. ksxt/async_/__pycache__/__init__.cpython-312.pyc +0 -0
  13. ksxt/async_/__pycache__/bithumb.cpython-312.pyc +0 -0
  14. ksxt/async_/__pycache__/koreainvest.cpython-312.pyc +0 -0
  15. ksxt/async_/__pycache__/upbit.cpython-312.pyc +0 -0
  16. ksxt/async_/base/__pycache__/__init__.cpython-312.pyc +0 -0
  17. ksxt/async_/base/__pycache__/async_exchange.cpython-312.pyc +0 -0
  18. ksxt/async_/base/__pycache__/throttler.cpython-312.pyc +0 -0
  19. ksxt/async_/base/async_exchange.py +17 -17
  20. ksxt/async_/bithumb.py +37 -19
  21. ksxt/async_/koreainvest.py +74 -6
  22. ksxt/async_/upbit.py +52 -20
  23. ksxt/base/__pycache__/__init__.cpython-312.pyc +0 -0
  24. ksxt/base/__pycache__/errors.cpython-312.pyc +0 -0
  25. ksxt/base/__pycache__/exchange.cpython-312.pyc +0 -0
  26. ksxt/base/__pycache__/rate_limiter.cpython-312.pyc +0 -0
  27. ksxt/base/__pycache__/rest_exchange.cpython-312.pyc +0 -0
  28. ksxt/base/__pycache__/types.cpython-312.pyc +0 -0
  29. ksxt/base/exchange.py +31 -23
  30. ksxt/base/rest_exchange.py +26 -28
  31. ksxt/bithumb.py +29 -12
  32. ksxt/config/__pycache__/__init__.cpython-312.pyc +0 -0
  33. ksxt/config/token.toml +6 -6
  34. ksxt/koreainvest.py +24 -6
  35. ksxt/market/__pycache__/base.cpython-312.pyc +0 -0
  36. ksxt/market/__pycache__/db.cpython-312.pyc +0 -0
  37. ksxt/market/__pycache__/logging.cpython-312.pyc +0 -0
  38. ksxt/market/__pycache__/manager.cpython-312.pyc +0 -0
  39. ksxt/market/__pycache__/markets.cpython-312.pyc +0 -0
  40. ksxt/market/krx/__pycache__/kosdaq.cpython-312.pyc +0 -0
  41. ksxt/market/krx/__pycache__/kospi.cpython-312.pyc +0 -0
  42. ksxt/market/krx/__pycache__/stock.cpython-312.pyc +0 -0
  43. ksxt/market/us/__pycache__/amex.cpython-312.pyc +0 -0
  44. ksxt/market/us/__pycache__/nasdaq.cpython-312.pyc +0 -0
  45. ksxt/market/us/__pycache__/nyse.cpython-312.pyc +0 -0
  46. ksxt/market/us/__pycache__/stock.cpython-312.pyc +0 -0
  47. ksxt/models/__pycache__/__init__.cpython-312.pyc +0 -0
  48. ksxt/models/__pycache__/balance.cpython-312.pyc +0 -0
  49. ksxt/models/__pycache__/cash.cpython-312.pyc +0 -0
  50. ksxt/models/__pycache__/common.cpython-312.pyc +0 -0
  51. ksxt/models/__pycache__/error.cpython-312.pyc +0 -0
  52. ksxt/models/__pycache__/historical.cpython-312.pyc +0 -0
  53. ksxt/models/__pycache__/market.cpython-312.pyc +0 -0
  54. ksxt/models/__pycache__/order.cpython-312.pyc +0 -0
  55. ksxt/models/__pycache__/orderbook.cpython-312.pyc +0 -0
  56. ksxt/models/__pycache__/ticker.cpython-312.pyc +0 -0
  57. ksxt/models/__pycache__/token.cpython-312.pyc +0 -0
  58. ksxt/models/__pycache__/transaction.cpython-312.pyc +0 -0
  59. ksxt/models/transaction.py +2 -0
  60. ksxt/parser/__pycache__/bithumb.cpython-312.pyc +0 -0
  61. ksxt/parser/__pycache__/koreainvest.cpython-312.pyc +0 -0
  62. ksxt/parser/__pycache__/parser.cpython-312.pyc +0 -0
  63. ksxt/parser/__pycache__/upbit.cpython-312.pyc +0 -0
  64. ksxt/parser/bithumb.py +83 -6
  65. ksxt/parser/koreainvest.py +56 -8
  66. ksxt/parser/parser.py +21 -5
  67. ksxt/parser/upbit.py +87 -10
  68. ksxt/upbit.py +42 -16
  69. ksxt/utils/__pycache__/safer.cpython-312.pyc +0 -0
  70. ksxt/utils/__pycache__/sorter.cpython-312.pyc +0 -0
  71. ksxt/utils/__pycache__/timer.cpython-312.pyc +0 -0
  72. {ksxt-1.0.4.dist-info → ksxt-1.0.5.dist-info}/METADATA +1 -1
  73. ksxt-1.0.5.dist-info/RECORD +123 -0
  74. {ksxt-1.0.4.dist-info → ksxt-1.0.5.dist-info}/WHEEL +1 -1
  75. ksxt-1.0.4.dist-info/RECORD +0 -120
  76. {ksxt-1.0.4.dist-info → ksxt-1.0.5.dist-info}/LICENSE.txt +0 -0
  77. {ksxt-1.0.4.dist-info → ksxt-1.0.5.dist-info}/top_level.txt +0 -0
ksxt/parser/bithumb.py CHANGED
@@ -1,4 +1,4 @@
1
- from datetime import datetime
1
+ from datetime import datetime, timezone
2
2
  from operator import attrgetter
3
3
  from typing import Dict, List, Optional
4
4
 
@@ -46,8 +46,22 @@ class BithumbParser(BaseParser):
46
46
  warning_code=False if safer.safe_string(market, "market_warning") == "NONE" else True,
47
47
  )
48
48
 
49
- def parse_historical_data(self, response: dict, symbol: str, base_market: str = "KRW") -> HistoricalDataInfo:
49
+ def parse_historical_data(
50
+ self,
51
+ response: List[Dict],
52
+ symbol: str,
53
+ start: datetime | None = None,
54
+ end: datetime | None = None,
55
+ base_market: str = "KRW",
56
+ ) -> HistoricalDataInfo:
50
57
  ohlcv = [self._parse_ohlcva(_, base_market) for _ in response]
58
+
59
+ # Filter by start and end datetime
60
+ if start:
61
+ ohlcv = [data for data in ohlcv if data.datetime >= start]
62
+ if end:
63
+ ohlcv = [data for data in ohlcv if data.datetime <= end]
64
+
51
65
  sorted_ohlcv = sorter.sort_by(ohlcv, key="datetime")
52
66
 
53
67
  security_name = symbol
@@ -256,8 +270,21 @@ class BithumbParser(BaseParser):
256
270
  market_ask_fee=safer.safe_number(response, "maker_ask_fee"),
257
271
  )
258
272
 
259
- def parse_open_order_history(self, response: dict, base_market: str = "KRW") -> OpenedOrderHistory:
273
+ def parse_open_order_history(
274
+ self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
275
+ ) -> OpenedOrderHistory:
260
276
  orders = [self._parse_open_order_info(order, base_market) for order in response]
277
+
278
+ # Filter by start and end datetime
279
+ if start:
280
+ orders = [
281
+ order for order in orders if order.created_at.astimezone(timezone.utc) >= start.astimezone(timezone.utc)
282
+ ]
283
+ if end:
284
+ orders = [
285
+ order for order in orders if order.created_at.astimezone(timezone.utc) <= end.astimezone(timezone.utc)
286
+ ]
287
+
261
288
  return OpenedOrderHistory(history=orders)
262
289
 
263
290
  def _parse_open_order_info(self, order: dict, base_market: str = "KRW") -> OpenedOrderInfo:
@@ -268,13 +295,27 @@ class BithumbParser(BaseParser):
268
295
  price=safer.safe_number(order, "price"),
269
296
  qty=safer.safe_number(order, "volume"),
270
297
  amount=safer.safe_number(order, "price") * safer.safe_number(order, "volume"),
298
+ tax=0,
271
299
  fee=safer.safe_number(order, "reserved_fee"),
272
300
  created_at=datetime.fromisoformat(safer.safe_string(order, "created_at")),
273
301
  order_type=safer.safe_string(order, "ord_type"),
274
302
  )
275
303
 
276
- def parse_closed_order_history(self, response: dict, base_market: str = "KRW") -> ClosedOrderHistory:
304
+ def parse_closed_order_history(
305
+ self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
306
+ ) -> ClosedOrderHistory:
277
307
  orders = [self._parse_closed_order_info(order, base_market) for order in response]
308
+
309
+ # Filter by start and end datetime
310
+ if start:
311
+ orders = [
312
+ order for order in orders if order.created_at.astimezone(timezone.utc) >= start.astimezone(timezone.utc)
313
+ ]
314
+ if end:
315
+ orders = [
316
+ order for order in orders if order.created_at.astimezone(timezone.utc) <= end.astimezone(timezone.utc)
317
+ ]
318
+
278
319
  return ClosedOrderHistory(history=orders)
279
320
 
280
321
  def _parse_closed_order_info(self, order: dict, base_market: str = "KRW") -> ClosedOrderInfo:
@@ -285,6 +326,7 @@ class BithumbParser(BaseParser):
285
326
  price=safer.safe_number(order, "price"),
286
327
  qty=safer.safe_number(order, "volume"),
287
328
  amount=safer.safe_number(order, "price") * safer.safe_number(order, "volume"),
329
+ tax=0,
288
330
  fee=safer.safe_number(order, "reserved_fee"),
289
331
  created_at=datetime.fromisoformat(safer.safe_string(order, "created_at")),
290
332
  order_type=safer.safe_string(order, "ord_type"),
@@ -296,12 +338,46 @@ class BithumbParser(BaseParser):
296
338
  def parse_create_order(self, response: dict, base_market: str = "KRW") -> CreateOrderResponse:
297
339
  return CreateOrderResponse(order_datetime=datetime.now(), order_id=safer.safe_string(response, "uuid"))
298
340
 
299
- def parse_withdrawal_history(self, response: List[Dict], base_market: str = "KRW") -> WithdrawalHistory:
341
+ def parse_withdrawal_history(
342
+ self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
343
+ ) -> WithdrawalHistory:
300
344
  parsed_items = [self._parse_transaction_history_item(item, base_market) for item in response]
345
+
346
+ # Filter by start and end datetime
347
+ if start:
348
+ parsed_items = [
349
+ item
350
+ for item in parsed_items
351
+ if item.created_at.astimezone(timezone.utc) >= start.astimezone(timezone.utc)
352
+ ]
353
+ if end:
354
+ parsed_items = [
355
+ item
356
+ for item in parsed_items
357
+ if item.created_at.astimezone(timezone.utc) <= end.astimezone(timezone.utc)
358
+ ]
359
+
301
360
  return WithdrawalHistory(history=parsed_items)
302
361
 
303
- def parse_deposit_history(self, response: List[Dict], base_market: str = "KRW") -> DepositHistory:
362
+ def parse_deposit_history(
363
+ self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
364
+ ) -> DepositHistory:
304
365
  parsed_items = [self._parse_transaction_history_item(item, base_market) for item in response]
366
+
367
+ # Filter by start and end datetime
368
+ if start:
369
+ parsed_items = [
370
+ item
371
+ for item in parsed_items
372
+ if item.created_at.astimezone(timezone.utc) >= start.astimezone(timezone.utc)
373
+ ]
374
+ if end:
375
+ parsed_items = [
376
+ item
377
+ for item in parsed_items
378
+ if item.created_at.astimezone(timezone.utc) <= end.astimezone(timezone.utc)
379
+ ]
380
+
305
381
  return DepositHistory(history=parsed_items)
306
382
 
307
383
  def _parse_transaction_history_item(self, item: Dict, base_market: str = "KRW") -> TransactionInfo:
@@ -309,6 +385,7 @@ class BithumbParser(BaseParser):
309
385
  uuid=safer.safe_string(item, "uuid"),
310
386
  type=safer.safe_string(item, "type"),
311
387
  amount=safer.safe_number(item, "amount"),
388
+ tax=0,
312
389
  fee=safer.safe_number(item, "fee"),
313
390
  currency=safer.safe_string(item, "currency"),
314
391
  created_at=datetime.fromisoformat(safer.safe_string(item, "created_at")),
@@ -52,9 +52,23 @@ class KoreaInvestParser(BaseParser):
52
52
  warning_code=safer.safe_boolean(market, " market_warning"),
53
53
  )
54
54
 
55
- def parse_historical_data(self, response: Dict, symbol: str, base_market: str = "KRW") -> HistoricalDataInfo:
55
+ def parse_historical_data(
56
+ self,
57
+ response: List[Dict],
58
+ symbol: str,
59
+ start: datetime | None = None,
60
+ end: datetime | None = None,
61
+ base_market: str = "KRW",
62
+ ) -> HistoricalDataInfo:
56
63
  safe_response = safer.safe_value(response, "output2")
57
64
  ohlcv = [self._parse_ohlcva(_, base_market) for _ in safe_response]
65
+
66
+ # Filter by start and end datetime
67
+ if start:
68
+ ohlcv = [data for data in ohlcv if data.datetime >= start]
69
+ if end:
70
+ ohlcv = [data for data in ohlcv if data.datetime <= end]
71
+
58
72
  sorted_ohlcv = sorter.sort_by(ohlcv, key="datetime")
59
73
 
60
74
  security_name = symbol
@@ -77,9 +91,23 @@ class KoreaInvestParser(BaseParser):
77
91
  acml_amount=safer.safe_number(ohlcva, "acml_tr_pbmn"),
78
92
  )
79
93
 
80
- def parse_historical_index_data(self, response: Dict, symbol: str, base_market: str = "KRW") -> HistoricalDataInfo:
94
+ def parse_historical_index_data(
95
+ self,
96
+ response: Dict,
97
+ symbol: str,
98
+ start: datetime | None = None,
99
+ end: datetime | None = None,
100
+ base_market: str = "KRW",
101
+ ) -> HistoricalDataInfo:
81
102
  safe_response = safer.safe_value(response, "output2")
82
103
  ohlcv = [self._parse_index_ohlcva(_, base_market) for _ in safe_response]
104
+
105
+ # Filter by start and end datetime
106
+ if start:
107
+ ohlcv = [data for data in ohlcv if data.datetime >= start]
108
+ if end:
109
+ ohlcv = [data for data in ohlcv if data.datetime <= end]
110
+
83
111
  sorted_ohlcv = sorter.sort_by(ohlcv, key="datetime")
84
112
 
85
113
  security_name = symbol
@@ -242,10 +270,19 @@ class KoreaInvestParser(BaseParser):
242
270
  market_ask_fee=0.0140527 + trading_fee,
243
271
  )
244
272
 
245
- def parse_closed_order_history(self, response: Dict, base_market: str = "KRW") -> ClosedOrderHistory:
273
+ def parse_closed_order_history(
274
+ self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
275
+ ) -> ClosedOrderHistory:
246
276
  safe_response = safer.safe_value(response, "output1")
247
- history = [self._parse_closed_order_info(_, base_market) for _ in safe_response]
248
- return ClosedOrderHistory(history=history)
277
+ orders = [self._parse_closed_order_info(_, base_market) for _ in safe_response]
278
+
279
+ # Filter by start and end datetime
280
+ if start:
281
+ orders = [order for order in orders if order.created_at >= start]
282
+ if end:
283
+ orders = [order for order in orders if order.created_at <= end]
284
+
285
+ return ClosedOrderHistory(history=orders)
249
286
 
250
287
  def _parse_closed_order_info(self, order: Dict, base_market: str = "KRW") -> ClosedOrderInfo:
251
288
  if safer.safe_string(order, "sll_buy_dvsn_cd") == "01":
@@ -260,16 +297,26 @@ class KoreaInvestParser(BaseParser):
260
297
  price=safer.safe_number(order, "avg_prvs"),
261
298
  qty=safer.safe_number(order, "tot_ccld_qty"),
262
299
  amount=safer.safe_number(order, "tot_ccld_amt"),
300
+ tax=0,
263
301
  # 단일 주문 이력에는 수수료 관련 정보가 없고, 조회한 전체 목록에 대해 수수료 정보만 존재한다.
264
302
  fee=0,
265
303
  created_at=datetime.fromisoformat(safer.safe_string(order, "ord_dt")),
266
304
  order_type=safer.safe_string(order, "avg_prvs"),
267
305
  )
268
306
 
269
- def parse_open_order_history(self, response: Dict, base_market: str = "KRW") -> OpenedOrderHistory:
307
+ def parse_open_order_history(
308
+ self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
309
+ ) -> OpenedOrderHistory:
270
310
  safe_response = safer.safe_value(response, "output1")
271
- history = [self._parse_open_order_info(_, base_market) for _ in safe_response]
272
- return OpenedOrderHistory(history=history)
311
+ orders = [self._parse_open_order_info(_, base_market) for _ in safe_response]
312
+
313
+ # Filter by start and end datetime
314
+ if start:
315
+ orders = [order for order in orders if order.created_at >= start]
316
+ if end:
317
+ orders = [order for order in orders if order.created_at <= end]
318
+
319
+ return OpenedOrderHistory(history=orders)
273
320
 
274
321
  def _parse_open_order_info(self, order: Dict, base_market: str = "KRW") -> OpenedOrderInfo:
275
322
  if safer.safe_string(order, "sll_buy_dvsn_cd") == "01":
@@ -284,6 +331,7 @@ class KoreaInvestParser(BaseParser):
284
331
  price=safer.safe_number(order, "avg_prvs"),
285
332
  qty=safer.safe_number(order, "tot_ccld_qty"),
286
333
  amount=safer.safe_number(order, "tot_ccld_amt"),
334
+ tax=0,
287
335
  # 단일 주문 이력에는 수수료 관련 정보가 없고, 조회한 전체 목록에 대해 수수료 정보만 존재한다.
288
336
  fee=0,
289
337
  created_at=datetime.fromisoformat(safer.safe_string(order, "ord_dt")),
ksxt/parser/parser.py CHANGED
@@ -1,3 +1,4 @@
1
+ from datetime import datetime
1
2
  from typing import Dict, List, Optional
2
3
  import inspect
3
4
 
@@ -30,7 +31,14 @@ class BaseParser:
30
31
  def _parse_market(self, market: Dict) -> SecurityData:
31
32
  self._raise_not_implemented_error()
32
33
 
33
- def parse_historical_data(self, response: dict, symbol: str, base_market: str = "KRW") -> HistoricalDataInfo:
34
+ def parse_historical_data(
35
+ self,
36
+ response: List[Dict],
37
+ symbol: str,
38
+ start: datetime | None = None,
39
+ end: datetime | None = None,
40
+ base_market: str = "KRW",
41
+ ) -> HistoricalDataInfo:
34
42
  self._raise_not_implemented_error()
35
43
 
36
44
  def _parse_ohlcva(self, ohlcva: dict, base_market: str = "KRW") -> OHLCVData:
@@ -79,13 +87,17 @@ class BaseParser:
79
87
  def parse_trade_fee(self, response: dict, base_market: str = "KRW") -> TradeFeeInfo:
80
88
  self._raise_not_implemented_error()
81
89
 
82
- def parse_open_order_history(self, response: dict, base_market: str = "KRW") -> OpenedOrderHistory:
90
+ def parse_open_order_history(
91
+ self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
92
+ ) -> OpenedOrderHistory:
83
93
  self._raise_not_implemented_error()
84
94
 
85
95
  def _parse_open_order_info(self, order: dict, base_market: str = "KRW") -> OpenedOrderInfo:
86
96
  self._raise_not_implemented_error()
87
97
 
88
- def parse_closed_order_history(self, response: dict, base_market: str = "KRW") -> ClosedOrderHistory:
98
+ def parse_closed_order_history(
99
+ self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
100
+ ) -> ClosedOrderHistory:
89
101
  self._raise_not_implemented_error()
90
102
 
91
103
  def _parse_closed_order_info(self, order: dict, base_market: str = "KRW") -> ClosedOrderInfo:
@@ -100,10 +112,14 @@ class BaseParser:
100
112
  def parse_create_order(self, response: dict, base_market: str = "KRW") -> CreateOrderResponse:
101
113
  self._raise_not_implemented_error()
102
114
 
103
- def parse_withdrawal_history(self, response: List[Dict], base_market: str = "KRW") -> WithdrawalHistory:
115
+ def parse_withdrawal_history(
116
+ self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
117
+ ) -> WithdrawalHistory:
104
118
  self._raise_not_implemented_error()
105
119
 
106
- def parse_deposit_history(self, response: List[Dict], base_market: str = "KRW") -> DepositHistory:
120
+ def parse_deposit_history(
121
+ self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
122
+ ) -> DepositHistory:
107
123
  self._raise_not_implemented_error()
108
124
 
109
125
  def _parse_transaction_history_item(self, item: Dict, base_market: str = "KRW") -> TransactionInfo:
ksxt/parser/upbit.py CHANGED
@@ -1,4 +1,4 @@
1
- from datetime import datetime
1
+ from datetime import datetime, timezone
2
2
  from operator import attrgetter
3
3
  from typing import Dict, List, Optional
4
4
 
@@ -56,8 +56,22 @@ class UpbitParser(BaseParser):
56
56
  warning_code=safer.safe_boolean(market, " market_warning"),
57
57
  )
58
58
 
59
- def parse_historical_data(self, response: List[Dict], symbol: str, base_market: str = "KRW") -> HistoricalDataInfo:
59
+ def parse_historical_data(
60
+ self,
61
+ response: List[Dict],
62
+ symbol: str,
63
+ start: datetime | None = None,
64
+ end: datetime | None = None,
65
+ base_market: str = "KRW",
66
+ ) -> HistoricalDataInfo:
60
67
  ohlcv = [self._parse_ohlcva(_, base_market) for _ in response]
68
+
69
+ # Filter by start and end datetime
70
+ if start:
71
+ ohlcv = [data for data in ohlcv if data.datetime.astimezone(timezone.utc) >= start.astimezone(timezone.utc)]
72
+ if end:
73
+ ohlcv = [data for data in ohlcv if data.datetime.astimezone(timezone.utc) <= end.astimezone(timezone.utc)]
74
+
61
75
  sorted_ohlcv = sorter.sort_by(ohlcv, key="datetime")
62
76
 
63
77
  security_name = symbol
@@ -262,8 +276,21 @@ class UpbitParser(BaseParser):
262
276
  market_ask_fee=safer.safe_number(response, "maker_ask_fee"),
263
277
  )
264
278
 
265
- def parse_closed_order_history(self, response: List[dict], base_market: str = "KRW") -> ClosedOrderHistory:
279
+ def parse_closed_order_history(
280
+ self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
281
+ ) -> ClosedOrderHistory:
266
282
  orders = [self._parse_closed_order_info(order, base_market) for order in response]
283
+
284
+ # Filter by start and end datetime
285
+ if start:
286
+ orders = [
287
+ order for order in orders if order.created_at.astimezone(timezone.utc) >= start.astimezone(timezone.utc)
288
+ ]
289
+ if end:
290
+ orders = [
291
+ order for order in orders if order.created_at.astimezone(timezone.utc) <= end.astimezone(timezone.utc)
292
+ ]
293
+
267
294
  return ClosedOrderHistory(history=orders)
268
295
 
269
296
  def _parse_closed_order_info(self, order: dict, base_market: str = "KRW") -> ClosedOrderInfo:
@@ -271,16 +298,30 @@ class UpbitParser(BaseParser):
271
298
  uuid=safer.safe_string(order, "uuid"),
272
299
  type=safer.safe_string(order, "side"),
273
300
  symbol=self.safe_symbol(base_market, safer.safe_string(order, "market")),
274
- price=safer.safe_number(order, "price"),
301
+ price=safer.safe_number(order, "executed_funds") / safer.safe_number(order, "volume"),
275
302
  qty=safer.safe_number(order, "volume"),
276
- amount=safer.safe_number(order, "price") * safer.safe_number(order, "volume"),
303
+ amount=safer.safe_number(order, "executed_funds"),
304
+ tax=0,
277
305
  fee=safer.safe_number(order, "paid_fee"), # Adjusted to 'paid_fee' to match provided data
278
306
  created_at=datetime.fromisoformat(safer.safe_string(order, "created_at")),
279
307
  order_type=safer.safe_string(order, "ord_type"),
280
308
  )
281
309
 
282
- def parse_open_order_history(self, response: List[dict], base_market: str = "KRW") -> OpenedOrderHistory:
310
+ def parse_open_order_history(
311
+ self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
312
+ ) -> OpenedOrderHistory:
283
313
  orders = [self._parse_open_order_info(order, base_market) for order in response]
314
+
315
+ # Filter by start and end datetime
316
+ if start:
317
+ orders = [
318
+ order for order in orders if order.created_at.astimezone(timezone.utc) >= start.astimezone(timezone.utc)
319
+ ]
320
+ if end:
321
+ orders = [
322
+ order for order in orders if order.created_at.astimezone(timezone.utc) <= end.astimezone(timezone.utc)
323
+ ]
324
+
284
325
  return OpenedOrderHistory(history=orders)
285
326
 
286
327
  def _parse_open_order_info(self, order: dict, base_market: str = "KRW") -> OpenedOrderInfo:
@@ -288,20 +329,55 @@ class UpbitParser(BaseParser):
288
329
  uuid=safer.safe_string(order, "uuid"),
289
330
  type=safer.safe_string(order, "side"),
290
331
  symbol=self.safe_symbol(base_market, safer.safe_string(order, "market")),
291
- price=safer.safe_number(order, "price"),
332
+ price=safer.safe_number(order, "executed_funds") / safer.safe_number(order, "volume"),
292
333
  qty=safer.safe_number(order, "volume"),
293
- amount=safer.safe_number(order, "price") * safer.safe_number(order, "volume"),
334
+ amount=safer.safe_number(order, "executed_funds"),
335
+ tax=0,
294
336
  fee=safer.safe_number(order, "reserved_fee"),
295
337
  created_at=datetime.fromisoformat(safer.safe_string(order, "created_at")),
296
338
  order_type=safer.safe_string(order, "ord_type"),
297
339
  )
298
340
 
299
- def parse_withdrawal_history(self, response: List[Dict], base_market: str = "KRW") -> WithdrawalHistory:
341
+ def parse_withdrawal_history(
342
+ self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
343
+ ) -> WithdrawalHistory:
300
344
  parsed_items = [self._parse_transaction_history_item(item, base_market) for item in response]
345
+
346
+ # Filter by start and end datetime
347
+ if start:
348
+ parsed_items = [
349
+ item
350
+ for item in parsed_items
351
+ if item.created_at.astimezone(timezone.utc) >= start.astimezone(timezone.utc)
352
+ ]
353
+ if end:
354
+ parsed_items = [
355
+ item
356
+ for item in parsed_items
357
+ if item.created_at.astimezone(timezone.utc) <= end.astimezone(timezone.utc)
358
+ ]
359
+
301
360
  return WithdrawalHistory(history=parsed_items)
302
361
 
303
- def parse_deposit_history(self, response: List[Dict], base_market: str = "KRW") -> DepositHistory:
362
+ def parse_deposit_history(
363
+ self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
364
+ ) -> DepositHistory:
304
365
  parsed_items = [self._parse_transaction_history_item(item, base_market) for item in response]
366
+
367
+ # Filter by start and end datetime
368
+ if start:
369
+ parsed_items = [
370
+ item
371
+ for item in parsed_items
372
+ if item.created_at.astimezone(timezone.utc) >= start.astimezone(timezone.utc)
373
+ ]
374
+ if end:
375
+ parsed_items = [
376
+ item
377
+ for item in parsed_items
378
+ if item.created_at.astimezone(timezone.utc) <= end.astimezone(timezone.utc)
379
+ ]
380
+
305
381
  return DepositHistory(history=parsed_items)
306
382
 
307
383
  def _parse_transaction_history_item(self, item: Dict, base_market: str = "KRW") -> TransactionInfo:
@@ -309,6 +385,7 @@ class UpbitParser(BaseParser):
309
385
  uuid=safer.safe_string(item, "uuid"),
310
386
  type=safer.safe_string(item, "type"),
311
387
  amount=safer.safe_number(item, "amount"),
388
+ tax=0,
312
389
  fee=safer.safe_number(item, "fee"),
313
390
  currency=safer.safe_string(item, "currency"),
314
391
  created_at=datetime.fromisoformat(safer.safe_string(item, "done_at")),
ksxt/upbit.py CHANGED
@@ -2,7 +2,7 @@ import hashlib
2
2
  import json
3
3
  import time
4
4
  import uuid
5
- from datetime import datetime
5
+ from datetime import datetime, timezone
6
6
  from typing import Any, Dict, List, Literal, Optional
7
7
  from urllib.parse import urlencode
8
8
 
@@ -123,10 +123,19 @@ class Upbit(RestExchange, ImplicitAPI):
123
123
  return ksxt.models.KsxtMarketResponse(header=common_header, response=common_response, info=parsed_info)
124
124
 
125
125
  def fetch_historical_data(
126
- self, symbol: str, time_frame: str, start: str | None = None, end: str | None = None, base_market: str = "KRW"
126
+ self,
127
+ symbol: str,
128
+ time_frame: str,
129
+ start: datetime | None = None,
130
+ end: datetime | None = None,
131
+ base_market: str = "KRW",
127
132
  ) -> ksxt.models.KsxtHistoricalDataResponse:
128
133
  params = {"market": self.safe_symbol(base_market, symbol), "count": 200}
129
134
 
135
+ if end:
136
+ end_utc = end.astimezone(timezone.utc)
137
+ params.update({"to": end_utc.strftime("%Y-%m-%d %H:%M:%S")})
138
+
130
139
  # TODO : time_frame 을 어떻게 고정시킬까? 우리는 분봉, 일봉, 주봉, 월봉 만 지원한다고 가정하면?
131
140
  if time_frame.endswith("m"):
132
141
  # TODO : parse number
@@ -152,9 +161,13 @@ class Upbit(RestExchange, ImplicitAPI):
152
161
  if common_response.success != "0":
153
162
  return ksxt.models.KsxtHistoricalDataResponse(header=common_header, response=common_response, info=None)
154
163
 
155
- parsed_info = self.parser.parse_historical_data(response=response, symbol=symbol, base_market=base_market)
164
+ parsed_response = self.parser.parse_historical_data(
165
+ response=response, symbol=symbol, start=start, end=end, base_market=base_market
166
+ )
156
167
 
157
- return ksxt.models.KsxtHistoricalDataResponse(header=common_header, response=common_response, info=parsed_info)
168
+ return ksxt.models.KsxtHistoricalDataResponse(
169
+ header=common_header, response=common_response, info=parsed_response
170
+ )
158
171
 
159
172
  def fetch_ticker(self, symbol: str, base_market: str = "KRW") -> ksxt.models.KsxtTickerResponse:
160
173
  params = {"markets": self.safe_symbol(base_market, symbol)} # 다중 조회 시, 콤마로 구분 ex. 'KRW-BTC, BTC-ETH'
@@ -335,8 +348,8 @@ class Upbit(RestExchange, ImplicitAPI):
335
348
  self,
336
349
  acc_num: str,
337
350
  symbol: Optional[str] = "",
338
- start: Optional[str] = None,
339
- end: Optional[str] = None,
351
+ start: datetime | None = None,
352
+ end: datetime | None = None,
340
353
  base_market: str = "KRW",
341
354
  ) -> ksxt.models.KsxtClosedOrderResponse:
342
355
  params = {"state": "done"}
@@ -352,16 +365,18 @@ class Upbit(RestExchange, ImplicitAPI):
352
365
  if common_response.success != "0":
353
366
  return ksxt.models.KsxtClosedOrderResponse(header=common_header, response=common_response, info=None)
354
367
 
355
- parsed_info = self.parser.parse_closed_order_history(response=response, base_market=base_market)
356
-
368
+ # 데이터 파싱
369
+ parsed_info = self.parser.parse_closed_order_history(
370
+ response=response, start=start, end=end, base_market=base_market
371
+ )
357
372
  return ksxt.models.KsxtClosedOrderResponse(header=common_header, response=common_response, info=parsed_info)
358
373
 
359
374
  def fetch_open_order(
360
375
  self,
361
376
  acc_num: str,
362
- symbol: str | None = "",
363
- start: str | None = None,
364
- end: str | None = None,
377
+ symbol: Optional[str] = "",
378
+ start: datetime | None = None,
379
+ end: datetime | None = None,
365
380
  base_market: str = "KRW",
366
381
  ) -> ksxt.models.KsxtOpenOrderResponse:
367
382
  params = {"state": "wait"}
@@ -377,12 +392,15 @@ class Upbit(RestExchange, ImplicitAPI):
377
392
  if common_response.success != "0":
378
393
  return ksxt.models.KsxtOpenOrderResponse(header=common_header, response=common_response, info=None)
379
394
 
380
- parsed_info = self.parser.parse_open_order_history(response=response, base_market=base_market)
395
+ # 데이터 파싱
396
+ parsed_info = self.parser.parse_open_order_history(
397
+ response=response, start=start, end=end, base_market=base_market
398
+ )
381
399
 
382
400
  return ksxt.models.KsxtOpenOrderResponse(header=common_header, response=common_response, info=parsed_info)
383
401
 
384
402
  def fetch_withdrawal_history(
385
- self, acc_num: str, base_market: str = "KRW"
403
+ self, acc_num: str, start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
386
404
  ) -> ksxt.models.KsxtWithdrawalHistoryResponse:
387
405
  params = {"currency": base_market, "state": "DONE"}
388
406
 
@@ -394,13 +412,18 @@ class Upbit(RestExchange, ImplicitAPI):
394
412
  if common_response.success != "0":
395
413
  return ksxt.models.KsxtWithdrawalHistoryResponse(header=common_header, response=common_response, info=None)
396
414
 
397
- parsed_info = self.parser.parse_withdrawal_history(response=response, base_market=base_market)
415
+ # 데이터 파싱
416
+ parsed_info = self.parser.parse_withdrawal_history(
417
+ response=response, start=start, end=end, base_market=base_market
418
+ )
398
419
 
399
420
  return ksxt.models.KsxtWithdrawalHistoryResponse(
400
421
  header=common_header, response=common_response, info=parsed_info
401
422
  )
402
423
 
403
- def fetch_deposit_history(self, acc_num: str, base_market: str = "KRW") -> ksxt.models.KsxtDepositHistoryResponse:
424
+ def fetch_deposit_history(
425
+ self, acc_num: str, start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
426
+ ) -> ksxt.models.KsxtDepositHistoryResponse:
404
427
  params = {"currency": base_market, "state": "ACCEPTED"}
405
428
 
406
429
  common_header = self.create_common_header(request_params=params)
@@ -411,7 +434,10 @@ class Upbit(RestExchange, ImplicitAPI):
411
434
  if common_response.success != "0":
412
435
  return ksxt.models.KsxtDepositHistoryResponse(header=common_header, response=common_response, info=None)
413
436
 
414
- parsed_info = self.parser.parse_deposit_history(response=response, base_market=base_market)
437
+ # 데이터 파싱
438
+ parsed_info = self.parser.parse_deposit_history(
439
+ response=response, start=start, end=end, base_market=base_market
440
+ )
415
441
 
416
442
  return ksxt.models.KsxtDepositHistoryResponse(header=common_header, response=common_response, info=parsed_info)
417
443
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ksxt
3
- Version: 1.0.4
3
+ Version: 1.0.5
4
4
  License: MIT License
5
5
 
6
6
  Copyright © 2023 AMOSA