pyqqq 0.12.169__tar.gz → 0.12.170__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.169 → pyqqq-0.12.170}/PKG-INFO +1 -1
  2. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyproject.toml +1 -1
  3. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/backtest/broker.py +48 -10
  4. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/backtest/environment.py +8 -3
  5. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/kis/domestic_stock.py +1 -1
  6. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/kis/simple.py +13 -3
  7. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/market_schedule.py +1 -1
  8. {pyqqq-0.12.169 → pyqqq-0.12.170}/README.md +0 -0
  9. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/__init__.py +0 -0
  10. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/ai/__init__.py +0 -0
  11. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/ai/daily.py +0 -0
  12. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/ai/domestic.py +0 -0
  13. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/ai/market_schedule.py +0 -0
  14. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/ai/minute.py +0 -0
  15. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/backtest/__init__.py +0 -0
  16. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/backtest/logger.py +0 -0
  17. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/backtest/positionprovider.py +0 -0
  18. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/backtest/strategy.py +0 -0
  19. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/backtest/utils.py +0 -0
  20. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/backtest/wallclock.py +0 -0
  21. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/__init__.py +0 -0
  22. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/ebest/__init__.py +0 -0
  23. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/ebest/domestic_stock.py +0 -0
  24. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/ebest/oauth.py +0 -0
  25. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/ebest/simple.py +0 -0
  26. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/ebest/tr_client.py +0 -0
  27. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/helper.py +0 -0
  28. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/kis/__init__.py +0 -0
  29. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/kis/oauth.py +0 -0
  30. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/kis/overseas_stock.py +0 -0
  31. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/kis/simple_overseas.py +0 -0
  32. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/kis/tr_client.py +0 -0
  33. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/multiprocess_tracker.py +0 -0
  34. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/brokerage/tracker.py +0 -0
  35. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/config.py +0 -0
  36. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/data/__init__.py +0 -0
  37. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/data/daily.py +0 -0
  38. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/data/domestic.py +0 -0
  39. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/data/index.py +0 -0
  40. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/data/minutes.py +0 -0
  41. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/data/overseas.py +0 -0
  42. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/data/realtime.py +0 -0
  43. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/data/ticks.py +0 -0
  44. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/data/us_stocks.py +0 -0
  45. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/datatypes.py +0 -0
  46. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/executors/__init__.py +0 -0
  47. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/executors/hook.py +0 -0
  48. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/__init__.py +0 -0
  49. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/api_client.py +0 -0
  50. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/array.py +0 -0
  51. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/casting.py +0 -0
  52. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/compute.py +0 -0
  53. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/copycat.py +0 -0
  54. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/daily_tickers.py +0 -0
  55. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/display.py +0 -0
  56. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/kvstore.py +0 -0
  57. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/limiter.py +0 -0
  58. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/local_cache.py +0 -0
  59. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/logger.py +0 -0
  60. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/mock_api.py +0 -0
  61. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/position_classifier.py +0 -0
  62. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/retry.py +0 -0
  63. {pyqqq-0.12.169 → pyqqq-0.12.170}/pyqqq/utils/singleton.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyqqq
3
- Version: 0.12.169
3
+ Version: 0.12.170
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.169"
3
+ version = "0.12.170"
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"
@@ -244,6 +244,7 @@ class TradingBroker(BaseBroker):
244
244
  data_api: Union[KISSimpleDomesticStock, KISSimpleOverseasStock],
245
245
  trading_api: Union[KISSimpleDomesticStock, KISSimpleOverseasStock],
246
246
  clock: WallClock,
247
+ market_nxt_on: bool = False,
247
248
  ):
248
249
  """TradingBroker 클래스의 초기화 메서드입니다.
249
250
 
@@ -260,25 +261,62 @@ class TradingBroker(BaseBroker):
260
261
  self.logger = get_logger("TradingBroker", clock)
261
262
  self.data_api = data_api
262
263
  self.trading_api = trading_api
264
+ self.market_nxt_on = market_nxt_on
263
265
 
264
266
  def get_account(self) -> dict:
265
267
  return self.trading_api.get_account()
266
268
 
267
- def get_price(self, code: str) -> Decimal:
268
- price_data = self.data_api.get_price(code)
269
+ def get_price(self, code: str, data_exchange: Optional[DataExchange] = None) -> Decimal:
270
+ if data_exchange:
271
+ price_data = self.data_api.get_price(code, data_exchange=data_exchange)
272
+ else:
273
+ if self.market_nxt_on:
274
+ price_data = self.data_api.get_price(code, data_exchange=DataExchange.NXT)
275
+ if price_data.get("current_price") == 0:
276
+ price_data = self.data_api.get_price(code, data_exchange=DataExchange.KRX)
277
+ else:
278
+ price_data = self.data_api.get_price(code, data_exchange=DataExchange.KRX)
279
+
269
280
  result = price_data.get("current_price")
270
281
  if result is None:
271
282
  raise ValueError(f"Current price not found: {code}")
272
283
  return Decimal(str(result))
273
284
 
274
- def get_minute_price(self, code) -> pd.DataFrame:
275
- return self.data_api.get_today_minute_data(code)
285
+ def get_minute_price(self, code: str, data_exchange: Optional[DataExchange] = None) -> pd.DataFrame:
286
+ if data_exchange:
287
+ result = self.data_api.get_today_minute_data(code, data_exchange=data_exchange)
288
+ else:
289
+ if self.market_nxt_on:
290
+ result = self.data_api.get_today_minute_data(code, data_exchange=DataExchange.NXT)
291
+ if result.iloc[-1].close == 0:
292
+ result = self.data_api.get_today_minute_data(code, data_exchange=DataExchange.KRX)
293
+ else:
294
+ result = self.data_api.get_today_minute_data(code, data_exchange=DataExchange.KRX)
295
+ return result
276
296
 
277
- def get_daily_price(self, code: str, from_date: dtm.date, to_date: dtm.date) -> pd.DataFrame:
278
- return self.data_api.get_historical_daily_data(code, from_date, to_date, adjusted_price=True)
297
+ def get_daily_price(self, code: str, from_date: dtm.date, to_date: dtm.date, data_exchange: Optional[DataExchange] = None) -> pd.DataFrame:
298
+ if data_exchange:
299
+ result = self.data_api.get_historical_daily_data(code, from_date, to_date, adjusted_price=True, data_exchange=data_exchange)
300
+ else:
301
+ if self.market_nxt_on:
302
+ result = self.data_api.get_historical_daily_data(code, from_date, to_date, adjusted_price=True, data_exchange=DataExchange.NXT)
303
+ if result.empty:
304
+ result = self.data_api.get_historical_daily_data(code, from_date, to_date, adjusted_price=True, data_exchange=DataExchange.KRX)
305
+ else:
306
+ result = self.data_api.get_historical_daily_data(code, from_date, to_date, adjusted_price=True, data_exchange=DataExchange.KRX)
307
+ return result
279
308
 
280
- def get_orderbook(self):
281
- return self.data_api.get_orderbook()
309
+ def get_orderbook(self, code: str, data_exchange: Optional[DataExchange] = None):
310
+ if data_exchange:
311
+ result = self.data_api.get_orderbook(code, data_exchange=data_exchange)
312
+ else:
313
+ if self.market_nxt_on:
314
+ result = self.data_api.get_orderbook(code, data_exchange=DataExchange.NXT)
315
+ if not result:
316
+ result = self.data_api.get_orderbook(code, data_exchange=DataExchange.KRX)
317
+ else:
318
+ result = self.data_api.get_orderbook(code, data_exchange=DataExchange.KRX)
319
+ return result
282
320
 
283
321
  def get_pending_orders(self):
284
322
  return self.trading_api.get_pending_orders()
@@ -842,8 +880,8 @@ class MockBroker(BaseBroker):
842
880
 
843
881
  nxt_pre_open_time = dtm.datetime.combine(today, dtm.time(8, 0, 0))
844
882
  nxt_pre_close_time = dtm.datetime.combine(today, dtm.time(8, 50, 0))
845
- nxt_main_open_time = dtm.datetime.combine(today, dtm.time(9, 0, 30))
846
- nxt_main_close_time = dtm.datetime.combine(today, dtm.time(15, 20, 0))
883
+ # nxt_main_open_time = dtm.datetime.combine(today, dtm.time(9, 0, 30))
884
+ # nxt_main_close_time = dtm.datetime.combine(today, dtm.time(15, 20, 0))
847
885
  nxt_after_open_time = dtm.datetime.combine(today, dtm.time(15, 40, 0))
848
886
  nxt_after_close_time = dtm.datetime.combine(today, dtm.time(20, 0, 0))
849
887
 
@@ -76,7 +76,9 @@ class BacktestEnvironment(TradingEnvironment):
76
76
  time_unit: str = "minutes",
77
77
  position_provider: BasePositionProvider = None,
78
78
  market: str = "kr_stock",
79
- current_data_handling: Literal["include", "virtual", "exclude"] = "virtual"
79
+ current_data_handling: Literal["include", "virtual", "exclude"] = "virtual",
80
+ local_cache_path: str = "./data",
81
+ market_nxt_on: bool = False,
80
82
  ):
81
83
  """BacktestEnvironment 클래스의 초기화 메서드입니다.
82
84
 
@@ -94,6 +96,8 @@ class BacktestEnvironment(TradingEnvironment):
94
96
  - "include": 현재(분)까지의 실제 데이터 반환
95
97
  - "virtual": 직전(1분전)까지의 데이터 + 현재(분)의 시가로 통일한 가상 데이터 반환 (기본값)
96
98
  - "exclude": 직전(1분전)까지의 데이터만 반환
99
+ local_cache_path (str, optional): 백테스트 사용 데이터 로컬 캐시 경로. Defaults to "./data".
100
+ market_nxt_on (bool, optional): NXT 시장 처리 여부. Defaults to False.
97
101
 
98
102
  Raises:
99
103
  AssertionError: start_time이 end_time보다 늦거나,
@@ -107,7 +111,7 @@ class BacktestEnvironment(TradingEnvironment):
107
111
  tzinfo = None if market == "kr_stock" else ZoneInfo("America/New_York")
108
112
 
109
113
  self.clock = WallClock(live_mode=False, start_time=start_time, end_time=end_time, tzinfo=tzinfo)
110
- self.broker = MockBroker(self.clock, position_provider, market, time_unit, current_data_handling)
114
+ 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)
111
115
 
112
116
 
113
117
  class KISDomesticEnvironment(TradingEnvironment):
@@ -138,7 +142,7 @@ class KISDomesticEnvironment(TradingEnvironment):
138
142
  ```
139
143
  """
140
144
 
141
- def __init__(self, paper_trading: bool = False):
145
+ def __init__(self, paper_trading: bool = False, market_nxt_on: bool = False):
142
146
  """KISDomesticEnvironment 클래스의 초기화 메서드입니다.
143
147
 
144
148
  Args:
@@ -155,6 +159,7 @@ class KISDomesticEnvironment(TradingEnvironment):
155
159
  data_api=conn.broker_simple,
156
160
  trading_api=conn.broker_simple if not paper_trading else conn.paper_broker_simple,
157
161
  clock=self.clock,
162
+ market_nxt_on=market_nxt_on,
158
163
  )
159
164
 
160
165
 
@@ -219,7 +219,7 @@ class KISDomesticStock:
219
219
  ]
220
220
 
221
221
  for k in date_keys:
222
- if k in output and len(output[k]) > 0:
222
+ if k in output and len(output[k]) > 0 and output[k] != '0': # NXT 종목이 아닌데 지정한 경우 output[k] 값이 0
223
223
  output[k] = dtm.datetime.strptime(output[k], "%Y%m%d").date()
224
224
 
225
225
  result = {"rt_cd": res_body["rt_cd"], "msg_cd": res_body["msg_cd"], "msg1": res_body["msg1"], "output": output}
@@ -4,7 +4,7 @@ from pyqqq.brokerage.kis.oauth import KISAuth
4
4
  from pyqqq.data.realtime import get_all_last_trades
5
5
  from pyqqq.datatypes import *
6
6
  from pyqqq.utils.logger import get_logger
7
- from pyqqq.utils.market_schedule import get_market_schedule
7
+ from pyqqq.utils.market_schedule import get_market_schedule, get_last_trading_day
8
8
  from pyqqq.utils.mock_api import with_mock
9
9
  from typing import AsyncGenerator, Dict, List, Optional
10
10
  import asyncio
@@ -263,8 +263,9 @@ class KISSimpleDomesticStock:
263
263
  result.extend(chunk)
264
264
 
265
265
  df = pd.DataFrame(result)
266
- df["date"] = pd.to_datetime(df["date"])
267
- df.set_index("date", inplace=True)
266
+ if not df.empty:
267
+ df["date"] = pd.to_datetime(df["date"])
268
+ df.set_index("date", inplace=True)
268
269
 
269
270
  return df
270
271
 
@@ -288,6 +289,13 @@ class KISSimpleDomesticStock:
288
289
  request_time = request_datetime.replace(second=0, microsecond=0)
289
290
  result = []
290
291
  schedule = get_market_schedule(dtm.date.today())
292
+ if schedule.full_day_closed:
293
+ _last_day = get_last_trading_day()
294
+ schedule = get_market_schedule(_last_day)
295
+ if data_exchange == DataExchange.NXT:
296
+ request_time = dtm.datetime.combine(_last_day, dtm.time(20, 0, 0))
297
+ else:
298
+ request_time = dtm.datetime.combine(_last_day, schedule.close_time)
291
299
 
292
300
  while True:
293
301
  r = self.stock_api.inquire_time_itemchartprice(
@@ -1110,6 +1118,8 @@ class KISSimpleDomesticStock:
1110
1118
  r = self.stock_api.inquire_asking_price_exp_ccn(asset_code, self._get_data_exchange(data_exchange))
1111
1119
 
1112
1120
  o1 = r["output1"]
1121
+ if not o1:
1122
+ return {}
1113
1123
 
1114
1124
  result = {
1115
1125
  "total_bid_volume": o1["total_bidp_rsqn"],
@@ -228,7 +228,7 @@ def _get_nxt_schedule(date: datetime.date) -> MarketSchedule:
228
228
  return schedule
229
229
 
230
230
 
231
- def get_last_trading_day(date: datetime.date = None, exchange: Union[str, Exchange] = "KRX") -> datetime.date:
231
+ def get_last_trading_day(date: Optional[datetime.date] = None, exchange: Union[str, Exchange] = "KRX") -> datetime.date:
232
232
  """
233
233
  주어진 날짜의 이전 거래일을 반환합니다.
234
234
 
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