pyqqq 0.12.185__tar.gz → 0.12.187__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.185 → pyqqq-0.12.187}/PKG-INFO +1 -1
  2. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyproject.toml +1 -1
  3. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/backtest/environment.py +7 -0
  4. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/brokerage/tracker.py +0 -8
  5. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/data/realtime.py +33 -6
  6. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/logger.py +53 -19
  7. pyqqq-0.12.185/pyqqq/ai/daily.py +0 -46
  8. pyqqq-0.12.185/pyqqq/ai/domestic.py +0 -57
  9. pyqqq-0.12.185/pyqqq/ai/market_schedule.py +0 -22
  10. pyqqq-0.12.185/pyqqq/ai/minute.py +0 -52
  11. pyqqq-0.12.185/pyqqq/utils/__init__.py +0 -0
  12. {pyqqq-0.12.185 → pyqqq-0.12.187}/README.md +0 -0
  13. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/__init__.py +0 -0
  14. {pyqqq-0.12.185/pyqqq/ai → pyqqq-0.12.187/pyqqq/backtest}/__init__.py +0 -0
  15. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/backtest/broker.py +0 -0
  16. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/backtest/logger.py +0 -0
  17. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/backtest/positionprovider.py +0 -0
  18. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/backtest/strategy.py +0 -0
  19. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/backtest/utils.py +0 -0
  20. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/backtest/wallclock.py +0 -0
  21. {pyqqq-0.12.185/pyqqq/backtest → pyqqq-0.12.187/pyqqq/brokerage}/__init__.py +0 -0
  22. {pyqqq-0.12.185/pyqqq/brokerage → pyqqq-0.12.187/pyqqq/brokerage/ebest}/__init__.py +0 -0
  23. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/brokerage/ebest/domestic_stock.py +0 -0
  24. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/brokerage/ebest/oauth.py +0 -0
  25. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/brokerage/ebest/simple.py +0 -0
  26. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/brokerage/ebest/tr_client.py +0 -0
  27. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/brokerage/helper.py +0 -0
  28. {pyqqq-0.12.185/pyqqq/brokerage/ebest → pyqqq-0.12.187/pyqqq/brokerage/kis}/__init__.py +0 -0
  29. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/brokerage/kis/domestic_stock.py +0 -0
  30. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/brokerage/kis/oauth.py +0 -0
  31. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/brokerage/kis/overseas_stock.py +0 -0
  32. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/brokerage/kis/simple.py +0 -0
  33. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/brokerage/kis/simple_overseas.py +0 -0
  34. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/brokerage/kis/tr_client.py +0 -0
  35. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/brokerage/multiprocess_tracker.py +0 -0
  36. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/config.py +0 -0
  37. {pyqqq-0.12.185/pyqqq/brokerage/kis → pyqqq-0.12.187/pyqqq/data}/__init__.py +0 -0
  38. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/data/daily.py +0 -0
  39. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/data/domestic.py +0 -0
  40. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/data/index.py +0 -0
  41. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/data/minutes.py +0 -0
  42. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/data/overseas.py +0 -0
  43. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/data/ticks.py +0 -0
  44. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/data/us_stocks.py +0 -0
  45. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/datatypes.py +0 -0
  46. {pyqqq-0.12.185/pyqqq/data → pyqqq-0.12.187/pyqqq/executors}/__init__.py +0 -0
  47. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/executors/hook.py +0 -0
  48. {pyqqq-0.12.185/pyqqq/executors → pyqqq-0.12.187/pyqqq/utils}/__init__.py +0 -0
  49. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/api_client.py +0 -0
  50. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/array.py +0 -0
  51. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/casting.py +0 -0
  52. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/compute.py +0 -0
  53. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/copycat.py +0 -0
  54. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/daily_tickers.py +0 -0
  55. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/display.py +0 -0
  56. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/kvstore.py +0 -0
  57. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/limiter.py +0 -0
  58. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/local_cache.py +0 -0
  59. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/market_schedule.py +0 -0
  60. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/mock_api.py +0 -0
  61. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/position_classifier.py +0 -0
  62. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/retry.py +0 -0
  63. {pyqqq-0.12.185 → pyqqq-0.12.187}/pyqqq/utils/singleton.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyqqq
3
- Version: 0.12.185
3
+ Version: 0.12.187
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.185"
3
+ version = "0.12.187"
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"
@@ -11,6 +11,8 @@ from pyqqq.brokerage.helper import EBestConnection, KISConnection
11
11
  from pyqqq.brokerage.kis.oauth import KISAuth
12
12
  from pyqqq.brokerage.kis.simple_overseas import KISSimpleOverseasStock
13
13
 
14
+ from pyqqq.utils.logger import get_handlers
15
+
14
16
 
15
17
  class TradingEnvironment(ABC):
16
18
  """트레이딩 환경을 정의하는 추상 기본 클래스입니다.
@@ -113,6 +115,11 @@ class BacktestEnvironment(TradingEnvironment):
113
115
  self.clock = WallClock(live_mode=False, start_time=start_time, end_time=end_time, tzinfo=tzinfo)
114
116
  self.broker = MockBroker(self.clock, position_provider, market, time_unit, current_data_handling, local_cache_path=local_cache_path, market_nxt_on=market_nxt_on)
115
117
 
118
+ # logger 에 백테스트 시간 정보 추간
119
+ handlers = get_handlers()
120
+ for handler in handlers:
121
+ handler.update_format(clock=self.clock)
122
+
116
123
 
117
124
  class KISDomesticEnvironment(TradingEnvironment):
118
125
  """한국투자증권 API를 사용한 국내주식 거래 환경입니다.
@@ -474,15 +474,7 @@ class TradingTracker:
474
474
  self.positions = self.simple_api.get_positions()
475
475
 
476
476
  def _refresh_pending_orders(self):
477
- old_pending_orders = {}
478
- for o in self.pending_orders:
479
- old_pending_orders[o.order_no] = o
480
-
481
477
  self.pending_orders = self.simple_api.get_pending_orders(exchanges=list(OrderExchange))
482
- for o in self.pending_orders:
483
- old_pending_order = old_pending_orders.get(o.order_no)
484
- if old_pending_order is not None:
485
- o.average_purchase_price = old_pending_order.average_purchase_price
486
478
 
487
479
  def _notify_pending_order_update(self, status: str, order: StockOrder):
488
480
  # deprecated
@@ -8,10 +8,11 @@ from typing import List, Union
8
8
  import websockets
9
9
 
10
10
  import pyqqq.config as c
11
- from pyqqq.datatypes import DataExchange
11
+ from pyqqq.datatypes import DataExchange, Exchange
12
12
  from pyqqq.utils.api_client import raise_for_status, send_request
13
13
  from pyqqq.utils.logger import get_logger
14
14
  from pyqqq.utils.singleton import singleton
15
+ from pyqqq.utils.market_schedule import is_trading_time, get_next_trading_day, get_market_schedule
15
16
 
16
17
  logger = get_logger(__name__)
17
18
 
@@ -136,6 +137,7 @@ class TickEventListener:
136
137
  self.retry_cnt = 0
137
138
  self.ws = None
138
139
  self.tot_retry_cnt = 0
140
+ self.reconnect_datetime = None
139
141
  logger.info(f"{self.LOG_TAG}init")
140
142
 
141
143
  if "event_add_delay" in config:
@@ -143,12 +145,12 @@ class TickEventListener:
143
145
 
144
146
  if "health_ping_delay" in config:
145
147
  self.health_ping_delay = config["health_ping_delay"]
146
-
148
+ now_int_str = str(int(dtm.datetime.now().timestamp()))
147
149
  if client_id is not None:
148
150
  api_key = os.getenv("PYQQQ_API_KEY") or ""
149
- self.client_id = client_id + api_key
151
+ self.client_id = client_id + api_key + "_" + now_int_str
150
152
  else:
151
- self.client_id = api_key
153
+ self.client_id = api_key + "_" + now_int_str
152
154
 
153
155
  self.url = url
154
156
  if self.url is None:
@@ -186,13 +188,37 @@ class TickEventListener:
186
188
  async for message in websocket:
187
189
  # logger.debug(f"{self.LOG_TAG}message: {message}")
188
190
  await self.handle_response(message)
191
+
192
+ logger.warning(f"{self.LOG_TAG}Websocket connection closed by server. code: {websocket.close_code}, reason: {websocket.close_reason}")
189
193
  except websockets.exceptions.ConnectionClosed:
190
194
  logger.info(f"{self.LOG_TAG}Connection closed")
191
- await self.on_connect_failed()
195
+ if websocket.close_code == 4409:
196
+ logger.info(f"{self.LOG_TAG}Connection closed by server with code: {websocket.close_code}, reason: {websocket.close_reason}")
197
+
198
+ self.move_events_to_pending()
199
+
200
+ if is_trading_time(dtm.datetime.now()):
201
+ logger.info("try reconnect next minute")
202
+ self.reconnect_datetime = dtm.datetime.now() + dtm.timedelta(minutes=1)
203
+ else:
204
+ logger.info("not trading time")
205
+ next_trading_day = get_next_trading_day(dtm.datetime.now().date())
206
+ next_market_schedule = get_market_schedule(next_trading_day, Exchange.NXT)
207
+ next_start_time = dtm.datetime.combine(next_trading_day, next_market_schedule.open_time)
208
+ logger.info(f"reconnect at {next_start_time}")
209
+ self.reconnect_datetime = next_start_time
210
+
211
+ await asyncio.sleep(self.reconnect_datetime.timestamp() - dtm.datetime.now().timestamp())
212
+
213
+ await self.connect_ws()
214
+
215
+ else:
216
+ await self.on_connect_failed()
192
217
  except Exception as e:
193
218
  logger.info(f"{self.LOG_TAG}Connection failed. error: {e}")
194
219
  await self.on_connect_failed()
195
220
  finally:
221
+ logger.info(f"{self.LOG_TAG}Websocket connection closed")
196
222
  self.ws_connected = False
197
223
 
198
224
  async def health_ping(self):
@@ -321,7 +347,8 @@ class TickEventListener:
321
347
  logger.debug(f"{self.LOG_TAG} try connect events:{cur_pending_event_ids}, connected: {res}")
322
348
 
323
349
  else:
324
- logger.debug(f"{self.LOG_TAG}not connected")
350
+ logger.info(f"{self.LOG_TAG} not connected. stop process_events_appending")
351
+ return
325
352
 
326
353
  await asyncio.sleep(self.event_add_delay)
327
354
 
@@ -1,30 +1,59 @@
1
1
  import logging
2
- from logging.handlers import TimedRotatingFileHandler
3
- import pyqqq.config as c
2
+ import logging.handlers
4
3
  import sys
4
+ from logging.handlers import TimedRotatingFileHandler
5
5
 
6
- _format = "%(levelname).1s %(name)s: %(message)s"
6
+ import pyqqq.config as c
7
+ from pyqqq.backtest.wallclock import WallClock
7
8
 
9
+ _default_format = "%(levelname).1s %(name)s: %(message)s"
8
10
  if not c.is_google_cloud_logging_enabled():
9
- _format = "%(asctime)s " + _format
11
+ _default_format = "%(asctime)s " + _default_format
12
+
13
+
14
+ class CustomHandler(logging.StreamHandler):
15
+ custom_format: str
16
+
17
+ class ClockFormatter(logging.Formatter):
18
+ def __init__(self, fmt=None, style="%", clock: WallClock = None):
19
+ super().__init__(fmt, style)
20
+ self.clock = clock or WallClock(live_mode=True)
21
+
22
+ def formatTime(self, record, datefmt=None):
23
+ dt = self.clock.now()
24
+ return dt.isoformat(timespec="milliseconds")
10
25
 
11
- _stdout_h = logging.StreamHandler(sys.stdout)
26
+ def __init__(self, stream):
27
+ super().__init__(stream)
28
+ self.custom_format = _default_format
29
+ self.setFormatter(logging.Formatter(self.custom_format))
30
+
31
+ def emit(self, record):
32
+ super().emit(record)
33
+
34
+ def update_format(self, format=None, clock: WallClock = None):
35
+ if format:
36
+ self.custom_format = format
37
+ self.setFormatter(self.ClockFormatter(self.custom_format, clock=clock))
38
+
39
+
40
+ _stdout_h = CustomHandler(sys.stdout)
12
41
  _stdout_h.setLevel(logging.DEBUG)
13
42
  _stdout_h.addFilter(lambda r: r.levelno <= logging.WARNING)
14
43
 
15
- _stderr_h = logging.StreamHandler(sys.stderr)
44
+ _stderr_h = CustomHandler(sys.stderr)
16
45
  _stderr_h.setLevel(logging.ERROR)
17
46
 
18
- logging.basicConfig(format=_format, handlers=[_stdout_h, _stderr_h])
47
+ logging.basicConfig(handlers=[_stdout_h, _stderr_h])
19
48
 
20
49
 
21
50
  def get_logger(
22
- name,
23
- level=logging.DEBUG,
24
- filename: str = None,
25
- when: str = 'h',
26
- interval: int = 1,
27
- backup_count: int = 24,
51
+ name,
52
+ level=logging.DEBUG,
53
+ filename: str = None,
54
+ when: str = "h",
55
+ interval: int = 1,
56
+ backup_count: int = 24,
28
57
  ) -> logging.Logger:
29
58
  """
30
59
  로깅을 위한 Logger 객체를 구성하고 반환합니다.
@@ -52,19 +81,17 @@ def get_logger(
52
81
  logger.setLevel(level)
53
82
 
54
83
  if not logger.handlers and filename:
55
- fh = TimedRotatingFileHandler(
56
- filename, when=when, backupCount=backup_count, interval=interval
57
- )
84
+ fh = TimedRotatingFileHandler(filename, when=when, backupCount=backup_count, interval=interval)
58
85
  fh.setLevel(level)
59
- fh.setFormatter(logging.Formatter(_format))
86
+ fh.setFormatter(logging.Formatter(_default_format))
60
87
  logger.addHandler(fh)
61
88
 
62
89
  return logger
63
90
 
64
91
 
65
92
  def get_bare_logger(
66
- name,
67
- level=logging.NOTSET,
93
+ name,
94
+ level=logging.NOTSET,
68
95
  ) -> logging.Logger:
69
96
  """
70
97
  level을 지정하지 않으면 NOTSET으로 설정되는 로거를 반환하기 위해 사용됩니다.
@@ -113,3 +140,10 @@ def set_module_logger_level(level, name="pyqqq"):
113
140
  for logger_name in logging.root.manager.loggerDict:
114
141
  if logger_name == name or logger_name.startswith(f"{name}."):
115
142
  logging.getLogger(logger_name).setLevel(level)
143
+
144
+
145
+ def get_handlers():
146
+ """
147
+ CustomHandler 클래스의 인스턴스를 반환합니다.
148
+ """
149
+ return [h for h in logging.root.handlers if isinstance(h, CustomHandler)]
@@ -1,46 +0,0 @@
1
- import datetime
2
- import pandas
3
- from typing import Dict, List, Optional
4
- import pyqqq.data.daily as daily
5
-
6
-
7
- def get_all_ohlcv_for_date(date: datetime.date, adjusted: bool = False) -> pandas.DataFrame:
8
- if isinstance(date, str):
9
- try:
10
- date = datetime.date.fromisoformat(date)
11
- except AttributeError:
12
- raise TypeError("Invalid date format. Please use 'YYYY-MM-DD' format.")
13
-
14
- df = daily.get_all_ohlcv_for_date(date, adjusted)
15
- if df.empty:
16
- raise ValueError(f"No OHLCV data found for {date}")
17
-
18
- return df
19
-
20
-
21
- def get_ohlcv_by_codes_for_period(
22
- codes: List[str],
23
- start_date: datetime.date,
24
- end_date: Optional[datetime.date] = None,
25
- adjusted: bool = False,
26
- ascending: bool = False,
27
- ) -> Dict[str, pandas.DataFrame]:
28
- if isinstance(start_date, str):
29
- try:
30
- start_date = datetime.date.fromisoformat(start_date)
31
- except AttributeError:
32
- raise TypeError("Invalid start_date format. Please use 'YYYY-MM-DD' format.")
33
- if isinstance(end_date, str):
34
- try:
35
- end_date = datetime.date.fromisoformat(end_date)
36
- except AttributeError:
37
- raise TypeError("Invalid end_date format. Please use 'YYYY-MM-DD' format.")
38
-
39
- dict = daily.get_ohlcv_by_codes_for_period(codes, start_date, end_date, adjusted, ascending)
40
- if not dict:
41
- if end_date:
42
- raise ValueError(f"No OHLCV data found for {start_date} to {end_date}")
43
- else:
44
- raise ValueError(f"No OHLCV data found for {start_date}")
45
-
46
- return dict
@@ -1,57 +0,0 @@
1
- import datetime
2
- import pandas
3
- from typing import List, Optional
4
- import pyqqq.data.domestic as domestic
5
-
6
-
7
- def get_tickers(date: Optional[datetime.date] = None, market: Optional[str] = None) -> pandas.DataFrame:
8
- if isinstance(date, str):
9
- try:
10
- date = datetime.date.fromisoformat(date)
11
- except AttributeError:
12
- raise TypeError("Invalid date format. Please use 'YYYY-MM-DD' format.")
13
-
14
- df = domestic.get_tickers(date, market)
15
- if df.empty:
16
- if date:
17
- raise ValueError(f"No tickers found for {date}")
18
- else:
19
- raise ValueError("No tickers found")
20
-
21
- return df
22
-
23
-
24
- def get_market_cap(date: datetime.date = None) -> pandas.DataFrame:
25
- if isinstance(date, str):
26
- try:
27
- date = datetime.date.fromisoformat(date)
28
- except AttributeError:
29
- raise TypeError("Invalid date format. Please use 'YYYY-MM-DD' format.")
30
-
31
- df = domestic.get_market_cap(date)
32
- if df.empty:
33
- if date:
34
- raise ValueError(f"No market cap data found for {date}")
35
- else:
36
- raise ValueError("No market cap data found")
37
-
38
- df.rename(columns={"value": "market_cap", "shares": "outstanding_shares"}, inplace=True)
39
- return df
40
-
41
-
42
- def get_market_cap_by_codes(codes: List[str], date: datetime.date = None) -> pandas.DataFrame:
43
- if isinstance(date, str):
44
- try:
45
- date = datetime.date.fromisoformat(date)
46
- except AttributeError:
47
- raise TypeError("Invalid date format. Please use 'YYYY-MM-DD' format.")
48
-
49
- df = domestic.get_market_cap_by_codes(codes, date)
50
- if df.empty:
51
- if date:
52
- raise ValueError(f"No market cap data found for {date}")
53
- else:
54
- raise ValueError("No market cap data found")
55
-
56
- df.rename(columns={"value": "market_cap", "shares": "outstanding_shares"}, inplace=True)
57
- return df
@@ -1,22 +0,0 @@
1
- import datetime
2
- import pyqqq.utils.market_schedule as market_schedule
3
-
4
-
5
- def get_last_trading_day(date: datetime.date = None, exchange: str = "KRX") -> str:
6
- if isinstance(date, str):
7
- try:
8
- date = datetime.date.fromisoformat(date)
9
- except AttributeError:
10
- raise TypeError("Invalid date format. Please use 'YYYY-MM-DD' format.")
11
-
12
- return market_schedule.get_last_trading_day(date, exchange).isoformat()
13
-
14
-
15
- def get_next_trading_day(date: datetime.date = None, exchange: str = "KRX") -> str:
16
- if isinstance(date, str):
17
- try:
18
- date = datetime.date.fromisoformat(date)
19
- except AttributeError:
20
- raise TypeError("Invalid date format. Please use 'YYYY-MM-DD' format.")
21
-
22
- return market_schedule.get_next_trading_day(date, exchange).isoformat()
@@ -1,52 +0,0 @@
1
- import datetime
2
- import pandas
3
- from typing import Dict, List
4
- import pyqqq.data.minutes as minutes
5
-
6
-
7
- def get_all_minute_data(time: datetime.datetime, source: str = "ebest", adjusted: bool = False) -> pandas.DataFrame:
8
- if isinstance(time, str):
9
- try:
10
- time = datetime.datetime.fromisoformat(time)
11
- except AttributeError:
12
- raise TypeError("Invalid time format. Please use 'YYYY-MM-DDTHH:MM' format.")
13
-
14
- df = minutes.get_all_minute_data(time, source, adjusted)
15
- if df.empty:
16
- raise ValueError(f"No minute data found for {time}")
17
-
18
- return df
19
-
20
-
21
- def get_all_day_data(
22
- date: datetime.date,
23
- codes: List[str] | str,
24
- period: datetime.timedelta = datetime.timedelta(minutes=1),
25
- source: str = "ebest",
26
- adjusted: bool = False,
27
- ascending: bool = True,
28
- ) -> Dict[str, pandas.DataFrame]:
29
- if isinstance(date, str):
30
- try:
31
- date = datetime.date.fromisoformat(date)
32
- except AttributeError:
33
- raise TypeError("Invalid date format. Please use 'YYYY-MM-DD' format.")
34
-
35
- result = minutes.get_all_day_data(date, codes, period, source, adjusted, ascending)
36
-
37
- # Ensure the result is a dict
38
- if isinstance(result, pandas.DataFrame):
39
- # Single DataFrame -> Convert to dict with one key
40
- if isinstance(codes, str):
41
- codes = [codes] # Handle single string case
42
- result = {codes[0]: result}
43
-
44
- if not result or all(df.empty for df in result.values()):
45
- raise ValueError(f"No minute data found for {date}")
46
-
47
- # Convert existing index to DatetimeIndex
48
- for code, df in result.items():
49
- if not isinstance(df.index, pandas.DatetimeIndex):
50
- df.index = pandas.to_datetime(df.index)
51
-
52
- return result
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