pyqqq 0.12.182__tar.gz → 0.12.184__tar.gz

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.

Potentially problematic release.


This version of pyqqq might be problematic. Click here for more details.

Files changed (63) hide show
  1. {pyqqq-0.12.182 → pyqqq-0.12.184}/PKG-INFO +1 -1
  2. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyproject.toml +3 -1
  3. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/kis/simple.py +9 -4
  4. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/data/domestic.py +12 -3
  5. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/daily_tickers.py +18 -4
  6. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/limiter.py +4 -2
  7. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/market_schedule.py +8 -1
  8. {pyqqq-0.12.182 → pyqqq-0.12.184}/README.md +0 -0
  9. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/__init__.py +0 -0
  10. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/ai/__init__.py +0 -0
  11. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/ai/daily.py +0 -0
  12. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/ai/domestic.py +0 -0
  13. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/ai/market_schedule.py +0 -0
  14. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/ai/minute.py +0 -0
  15. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/backtest/__init__.py +0 -0
  16. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/backtest/broker.py +0 -0
  17. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/backtest/environment.py +0 -0
  18. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/backtest/logger.py +0 -0
  19. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/backtest/positionprovider.py +0 -0
  20. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/backtest/strategy.py +0 -0
  21. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/backtest/utils.py +0 -0
  22. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/backtest/wallclock.py +0 -0
  23. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/__init__.py +0 -0
  24. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/ebest/__init__.py +0 -0
  25. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/ebest/domestic_stock.py +0 -0
  26. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/ebest/oauth.py +0 -0
  27. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/ebest/simple.py +0 -0
  28. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/ebest/tr_client.py +0 -0
  29. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/helper.py +0 -0
  30. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/kis/__init__.py +0 -0
  31. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/kis/domestic_stock.py +0 -0
  32. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/kis/oauth.py +0 -0
  33. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/kis/overseas_stock.py +0 -0
  34. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/kis/simple_overseas.py +0 -0
  35. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/kis/tr_client.py +0 -0
  36. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/multiprocess_tracker.py +0 -0
  37. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/brokerage/tracker.py +0 -0
  38. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/config.py +0 -0
  39. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/data/__init__.py +0 -0
  40. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/data/daily.py +0 -0
  41. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/data/index.py +0 -0
  42. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/data/minutes.py +0 -0
  43. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/data/overseas.py +0 -0
  44. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/data/realtime.py +0 -0
  45. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/data/ticks.py +0 -0
  46. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/data/us_stocks.py +0 -0
  47. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/datatypes.py +0 -0
  48. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/executors/__init__.py +0 -0
  49. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/executors/hook.py +0 -0
  50. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/__init__.py +0 -0
  51. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/api_client.py +0 -0
  52. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/array.py +0 -0
  53. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/casting.py +0 -0
  54. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/compute.py +0 -0
  55. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/copycat.py +0 -0
  56. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/display.py +0 -0
  57. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/kvstore.py +0 -0
  58. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/local_cache.py +0 -0
  59. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/logger.py +0 -0
  60. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/mock_api.py +0 -0
  61. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/position_classifier.py +0 -0
  62. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/retry.py +0 -0
  63. {pyqqq-0.12.182 → pyqqq-0.12.184}/pyqqq/utils/singleton.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyqqq
3
- Version: 0.12.182
3
+ Version: 0.12.184
4
4
  Summary: Package for quantitative strategy development on the PyQQQ platform
5
5
  License: MIT
6
6
  Author: PyQQQ team
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pyqqq"
3
- version = "0.12.182"
3
+ version = "0.12.184"
4
4
  description = "Package for quantitative strategy development on the PyQQQ platform"
5
5
  authors = ["PyQQQ team <pyqqq.cs@gmail.com>"]
6
6
  readme = "README.md"
@@ -30,6 +30,8 @@ ipykernel = "^6.29.4"
30
30
  pydata-sphinx-theme = "^0.15.2"
31
31
  sphinx-copybutton = "^0.5.2"
32
32
  sphinx-togglebutton = "^0.3.2"
33
+ yappi = "^1.6.10"
34
+ snakeviz = "^2.2.2"
33
35
 
34
36
  [tool.black]
35
37
  line-length = 240
@@ -1,15 +1,18 @@
1
+ import asyncio
2
+ import datetime as dtm
1
3
  from decimal import Decimal
4
+ from typing import AsyncGenerator, Dict, List, Optional
5
+
6
+ import pandas as pd
7
+
2
8
  from pyqqq.brokerage.kis.domestic_stock import KISDomesticStock
3
9
  from pyqqq.brokerage.kis.oauth import KISAuth
4
10
  from pyqqq.data.realtime import get_all_last_trades
5
11
  from pyqqq.datatypes import *
12
+ from pyqqq.utils.limiter import CallLimiter
6
13
  from pyqqq.utils.logger import get_logger
7
14
  from pyqqq.utils.market_schedule import get_market_schedule, is_full_day_closed
8
15
  from pyqqq.utils.mock_api import with_mock
9
- from typing import AsyncGenerator, Dict, List, Optional
10
- import asyncio
11
- import datetime as dtm
12
- import pandas as pd
13
16
 
14
17
 
15
18
  class KISStockPosition(StockPosition):
@@ -336,6 +339,8 @@ class KISSimpleDomesticStock:
336
339
  request_time = request_datetime.replace(hour=schedule.close_time.hour, minute=schedule.close_time.minute)
337
340
 
338
341
  while True:
342
+ CallLimiter().wait_limit_rate(20, scope="kis/inquire_time_itemchartprice")
343
+
339
344
  r = self.stock_api.inquire_time_itemchartprice(
340
345
  asset_code,
341
346
  request_time.time(),
@@ -244,7 +244,9 @@ def get_tickers(
244
244
 
245
245
  KRX 거래소에서는 거래정지 종목 등이 포함되어 있으나, NXT 거래소에서는 거래정지 종목이 제외되어 있습니다.
246
246
 
247
- 2018년 1월 1일 데이터 부터 조회 가능합니다. 수정주가는 소수점 첫째 자리에서 반올림합니다.
247
+ KRX 거래소는 2018년 1월 1일 데이터 부터 조회 가능합니다. 수정주가는 소수점 첫째 자리에서 반올림합니다.
248
+
249
+ NXT 거래소는 2025년 3월 4일 데이터 부터 조회 가능합니다. 수정주가는 소수점 첫째 자리에서 반올림합니다.
248
250
 
249
251
  Args:
250
252
  date (Optional[dtm.date]): 조회할 날짜. 기본값은 현재 날짜입니다.
@@ -265,7 +267,7 @@ def get_tickers(
265
267
  - listing_date (str or None): 상장일
266
268
 
267
269
  Raises:
268
- AssertionError: 잘못된 시장 이름이 입력된 경우 오류를 발생시킵니다.
270
+ AssertionError: 잘못된 시장이나 거래소 이름이 입력된 경우 오류를 발생시킵니다.
269
271
  HTTPError: API 요청이 실패했을 때 발생.
270
272
 
271
273
  Examples:
@@ -285,7 +287,10 @@ def get_tickers(
285
287
  "KOSDAQ",
286
288
  ], "market은 'KOSPI' 또는 'KOSDAQ'이어야 합니다."
287
289
 
288
- # TODO: NXT 거래소 2025년 3월 4일 이전 요청 시 에러 발생
290
+ assert exchange in [
291
+ "KRX", "NXT", DataExchange.KRX, DataExchange.NXT
292
+ ], "exchange는 'KRX' 또는 'NXT'이어야 합니다."
293
+
289
294
  exchange = DataExchange.validate(exchange)
290
295
 
291
296
  if date is None:
@@ -294,6 +299,10 @@ def get_tickers(
294
299
  if schedule.full_day_closed:
295
300
  date = get_last_trading_day(date)
296
301
 
302
+ # 2025년 3월 4일 이전 NXT 거래소 요청 시 빈 데이터 반환
303
+ if exchange == DataExchange.NXT and date < dtm.date(2025, 3, 4):
304
+ return pd.DataFrame()
305
+
297
306
  return _get_tickers(date, market, adjusted, exchange)
298
307
 
299
308
 
@@ -28,6 +28,7 @@ class DailyTickers:
28
28
  self._date = get_last_trading_day(self._today)
29
29
  else:
30
30
  self._date = self._today
31
+ self._is_before_today_krx_open = True
31
32
  self._tickers = None
32
33
  self._change_date()
33
34
 
@@ -36,12 +37,12 @@ class DailyTickers:
36
37
 
37
38
  @lru_cache(maxsize=30) # memory_profiler 로 확인 결과 하루치 fetch 결과가 약 2.5MiB
38
39
  @staticmethod
39
- def fetch_tickers(date: dtm.date) -> pd.DataFrame:
40
+ def fetch_tickers(date: dtm.date, exchange: str) -> pd.DataFrame:
40
41
  DailyTickers.logger.debug(f'\tfetch_tickers date={date} cache_info={DailyTickers.fetch_tickers.cache_info()}')
41
- return domestic.get_tickers(date)
42
+ return domestic.get_tickers(date, exchange=exchange)
42
43
 
43
44
  def _change_date(self, date: Optional[dtm.date] = None, force: bool = False):
44
- """_tickers 가 비었거나 날짜가 바뀌었으면 새로 채워넣는다."""
45
+ """_tickers 가 비었거나 날짜가 바뀌었으면 새로 채워넣는다. 8시 50분 이전에는 NXT 거래소 데이터를 사용한다."""
45
46
  if not date:
46
47
  if self._chk_days_passed():
47
48
  self._today = dtm.date.today()
@@ -52,14 +53,27 @@ class DailyTickers:
52
53
  else:
53
54
  date = self._date
54
55
 
56
+ if self._today == date:
57
+ if dtm.datetime.now().time() < dtm.time(8, 50) and not self._is_before_today_krx_open:
58
+ self._is_before_today_krx_open = True
59
+ self._tickers = None
60
+ elif dtm.datetime.now().time() >= dtm.time(8, 50) and self._is_before_today_krx_open:
61
+ self._is_before_today_krx_open = False
62
+ self._tickers = None
63
+ else:
64
+ pass
65
+ else:
66
+ self._is_before_today_krx_open = False
67
+
55
68
  if force:
56
69
  DailyTickers.fetch_tickers.cache_clear()
57
70
  self._tickers = None
58
71
 
59
72
  if self._tickers is None or self._date != date:
60
73
  self._date = date
74
+ exchange = "NXT" if self._is_before_today_krx_open else "KRX"
61
75
  self.logger.debug(f'\tdate changed. date={self._date} wait for get_tickers()')
62
- self._tickers = DailyTickers.fetch_tickers(self._date)
76
+ self._tickers = DailyTickers.fetch_tickers(self._date, exchange=exchange)
63
77
 
64
78
  def get_tickers(self, date: Optional[dtm.date] = None, force: bool = False) -> pd.DataFrame:
65
79
  """
@@ -3,7 +3,7 @@ import time
3
3
 
4
4
  _MAX_CALLS = 5
5
5
  _PERIOD = 1
6
- _SCOPE = 'default'
6
+ _SCOPE = "default"
7
7
 
8
8
 
9
9
  class CallLimiter:
@@ -13,12 +13,13 @@ class CallLimiter:
13
13
  이 클래스는 API 호출 빈도를 제한하여 너무 많은 요청으로 인해 서비스 제한을 받는 것을 방지합니다.
14
14
  이 클래스의 인스턴스는 전역에서 단 하나만 존재하며, 다양한 'scope'에 대한 호출 제한 윈도우를 관리합니다.
15
15
  """
16
+
16
17
  _instance = None
17
18
 
18
19
  def __new__(cls):
19
20
  if cls._instance is None:
20
21
  cls._instance = super(CallLimiter, cls).__new__(cls)
21
- cls._instance.windows = {'default': []}
22
+ cls._instance.windows = {"default": []}
22
23
 
23
24
  return cls._instance
24
25
 
@@ -63,6 +64,7 @@ def limit_calls(max_calls: int = _MAX_CALLS, period: float = _PERIOD, scope: str
63
64
  period (float): 기간(초) 동안의 호출 횟수를 제한합니다.
64
65
  scope (str): 호출 제한을 적용할 스코프 이름입니다.
65
66
  """
67
+
66
68
  def decorator(func):
67
69
  def wrapper(*args, **kwargs):
68
70
  CallLimiter().wait_limit_rate(max_calls=max_calls, period=period, scope=scope)
@@ -144,7 +144,14 @@ def is_trading_time(
144
144
  if exchange == Exchange.NXT and now.date() < datetime.date(2025, 3, 4):
145
145
  raise ValueError("NXT 거래소는 2025년 3월 4일 부터 운영되었습니다. 이전 날짜는 지원하지 않습니다.")
146
146
 
147
- return not (is_full_day_closed(now, exchange) or is_before_opening(now, exchange) or is_after_closing(now, exchange))
147
+ schedule = get_market_schedule(now.date(), exchange)
148
+ if schedule.full_day_closed:
149
+ return False
150
+
151
+ if now.time() < schedule.open_time or now.time() > schedule.close_time:
152
+ return False
153
+
154
+ return True
148
155
 
149
156
 
150
157
  def get_market_schedule(
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes