pyqqq 0.12.177__tar.gz → 0.12.179__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.177 → pyqqq-0.12.179}/PKG-INFO +1 -1
  2. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyproject.toml +1 -1
  3. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/backtest/broker.py +33 -1
  4. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/backtest/environment.py +10 -2
  5. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/kis/simple.py +5 -0
  6. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/data/minutes.py +4 -4
  7. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/position_classifier.py +1 -1
  8. {pyqqq-0.12.177 → pyqqq-0.12.179}/README.md +0 -0
  9. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/__init__.py +0 -0
  10. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/ai/__init__.py +0 -0
  11. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/ai/daily.py +0 -0
  12. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/ai/domestic.py +0 -0
  13. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/ai/market_schedule.py +0 -0
  14. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/ai/minute.py +0 -0
  15. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/backtest/__init__.py +0 -0
  16. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/backtest/logger.py +0 -0
  17. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/backtest/positionprovider.py +0 -0
  18. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/backtest/strategy.py +0 -0
  19. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/backtest/utils.py +0 -0
  20. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/backtest/wallclock.py +0 -0
  21. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/__init__.py +0 -0
  22. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/ebest/__init__.py +0 -0
  23. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/ebest/domestic_stock.py +0 -0
  24. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/ebest/oauth.py +0 -0
  25. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/ebest/simple.py +0 -0
  26. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/ebest/tr_client.py +0 -0
  27. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/helper.py +0 -0
  28. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/kis/__init__.py +0 -0
  29. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/kis/domestic_stock.py +0 -0
  30. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/kis/oauth.py +0 -0
  31. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/kis/overseas_stock.py +0 -0
  32. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/kis/simple_overseas.py +0 -0
  33. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/kis/tr_client.py +0 -0
  34. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/multiprocess_tracker.py +0 -0
  35. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/brokerage/tracker.py +0 -0
  36. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/config.py +0 -0
  37. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/data/__init__.py +0 -0
  38. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/data/daily.py +0 -0
  39. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/data/domestic.py +0 -0
  40. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/data/index.py +0 -0
  41. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/data/overseas.py +0 -0
  42. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/data/realtime.py +0 -0
  43. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/data/ticks.py +0 -0
  44. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/data/us_stocks.py +0 -0
  45. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/datatypes.py +0 -0
  46. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/executors/__init__.py +0 -0
  47. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/executors/hook.py +0 -0
  48. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/__init__.py +0 -0
  49. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/api_client.py +0 -0
  50. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/array.py +0 -0
  51. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/casting.py +0 -0
  52. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/compute.py +0 -0
  53. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/copycat.py +0 -0
  54. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/daily_tickers.py +0 -0
  55. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/display.py +0 -0
  56. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/kvstore.py +0 -0
  57. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/limiter.py +0 -0
  58. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/local_cache.py +0 -0
  59. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/logger.py +0 -0
  60. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/market_schedule.py +0 -0
  61. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/mock_api.py +0 -0
  62. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/retry.py +0 -0
  63. {pyqqq-0.12.177 → pyqqq-0.12.179}/pyqqq/utils/singleton.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyqqq
3
- Version: 0.12.177
3
+ Version: 0.12.179
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.177"
3
+ version = "0.12.179"
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"
@@ -25,6 +25,7 @@ from pyqqq.data.us_stocks import get_ticker_info as get_us_ticker_info
25
25
  from pyqqq.datatypes import *
26
26
  from pyqqq.utils.casting import casting
27
27
  from pyqqq.utils.market_schedule import get_last_trading_day, get_market_schedule, get_trading_day_with_offset
28
+ from pyqqq.utils.position_classfier import PositionClassifier
28
29
 
29
30
  MarketType = Literal["kr_stock", "us_stock"]
30
31
 
@@ -249,6 +250,8 @@ class TradingBroker(BaseBroker):
249
250
  trading_api: Union[KISSimpleDomesticStock, KISSimpleOverseasStock],
250
251
  clock: WallClock,
251
252
  market_nxt_on: bool = False,
253
+ strategy_name: Optional[str] = None,
254
+ classifier_type: Optional[str] = None,
252
255
  ):
253
256
  """TradingBroker 클래스의 초기화 메서드입니다.
254
257
 
@@ -266,6 +269,14 @@ class TradingBroker(BaseBroker):
266
269
  self.data_api = data_api
267
270
  self.trading_api = trading_api
268
271
  self.market_nxt_on = market_nxt_on
272
+ self.position_classifier = None
273
+
274
+ if classifier_type and classifier_type in ["auto", "direct"]:
275
+ if strategy_name:
276
+ kv_store_collection = f"{strategy_name}_classifier"
277
+ else:
278
+ kv_store_collection = f"account_{self.trading_api.account_no}_classifier"
279
+ self.position_classifier = PositionClassifier(simple_data_api=self.trading_api, kv_store_collection=kv_store_collection, default_type=classifier_type)
269
280
 
270
281
  def get_account(self) -> dict:
271
282
  return self.trading_api.get_account()
@@ -314,6 +325,7 @@ class TradingBroker(BaseBroker):
314
325
  result = self.data_api.get_historical_daily_data(code, from_date, to_date, adjusted_price=True, data_exchange=DataExchange.KRX)
315
326
  else:
316
327
  result = self.data_api.get_historical_daily_data(code, from_date, to_date, adjusted_price=True, data_exchange=DataExchange.KRX)
328
+ result = result.sort_index(ascending=True)
317
329
  return result
318
330
 
319
331
  def get_orderbook(self, code: str, data_exchange: Optional[DataExchange] = None):
@@ -349,7 +361,20 @@ class TradingBroker(BaseBroker):
349
361
 
350
362
  def create_order(self, asset_code: str, side: OrderSide, quantity: int, order_type: OrderType = OrderType.MARKET, price: int | Decimal = 0, exchange: OrderExchange = OrderExchange.KRX) -> str:
351
363
  self.logger.debug(f"create_order: {asset_code} {side} {quantity} {order_type} {price} {exchange}")
352
- return self.trading_api.create_order(asset_code, side, quantity, order_type, price, exchange=exchange)
364
+
365
+ if side is OrderSide.SELL and self.position_classifier:
366
+ # 포지션 분류기를 사용하여 전략에서 보유중인 수량으로 매도 가능 수량 재 계산
367
+ calc_quantity = self.position_classifier.get_sellable_quantity(asset_code, quantity)
368
+
369
+ if calc_quantity != quantity:
370
+ self.logger.warning(f"Requested sell quantity {quantity} for {asset_code} adjusted to {calc_quantity} based on position classifier.")
371
+ quantity = calc_quantity
372
+
373
+ order_no = self.trading_api.create_order(asset_code, side, quantity, order_type, price, exchange=exchange)
374
+ if order_no and self.position_classifier:
375
+ # 주문이 성공적으로 생성되면 포지션 분류기에 주문 정보를 업데이트
376
+ self.position_classifier.tagging_order_auto(order_no)
377
+ return order_no
353
378
 
354
379
  def update_order(self, org_order_no: str, order_type: OrderType, price: int | Decimal, quantity: int = 0, exchange: OrderExchange = OrderExchange.KRX):
355
380
  if isinstance(self.trading_api, KISSimpleOverseasStock):
@@ -386,6 +411,13 @@ class TradingBroker(BaseBroker):
386
411
  self.logger.debug(f"cancel_order: {order_no} {quantity}")
387
412
  return self.trading_api.cancel_order(order_no, quantity)
388
413
 
414
+ async def start_classifier(self):
415
+ """ 포지션 분류기를 시작합니다.
416
+ 이 메서드는 포지션 분류기를 사용한다면 반드시 호출해야 합니다.
417
+ """
418
+ if self.position_classifier:
419
+ await self.position_classifier.start()
420
+
389
421
 
390
422
  # 백테스팅 할때 사용되는 브로커
391
423
  class MockBroker(BaseBroker):
@@ -1,7 +1,7 @@
1
1
  import datetime as dtm
2
2
  import os
3
3
  from abc import ABC
4
- from typing import Literal
4
+ from typing import Literal, Optional
5
5
  from zoneinfo import ZoneInfo
6
6
 
7
7
  from pyqqq.backtest.broker import BaseBroker, MockBroker, TradingBroker
@@ -142,12 +142,18 @@ class KISDomesticEnvironment(TradingEnvironment):
142
142
  ```
143
143
  """
144
144
 
145
- def __init__(self, paper_trading: bool = False, market_nxt_on: bool = False):
145
+ def __init__(self, paper_trading: bool = False, market_nxt_on: bool = False, strategy_name: Optional[str] = None, classifier_type: Optional[str] = None):
146
146
  """KISDomesticEnvironment 클래스의 초기화 메서드입니다.
147
147
 
148
148
  Args:
149
149
  paper_trading (bool, optional): 모의투자 사용 여부. Defaults to False.
150
150
  True일 경우 모의투자 계좌 사용
151
+ market_nxt_on (bool, optional): NXT 시장 처리 여부. Defaults to False.
152
+ strategy_name (str, optional): 전략 이름. Defaults to None.
153
+ position classifier 데이터가 저장되는 kvstore collection 이름에 사용됨.
154
+ classifier_type (str, optional): "direct", "auto" 중 하나로 설정시 position classifier 사용하게 됨. Defaults to None.
155
+ 해당 값은 분류기가 기본 포지션을 어떤 방식으로 결정할지에 대한 설정.
156
+
151
157
 
152
158
  Raises:
153
159
  KeyError: 필요한 환경변수가 설정되지 않은 경우
@@ -160,6 +166,8 @@ class KISDomesticEnvironment(TradingEnvironment):
160
166
  trading_api=conn.broker_simple if not paper_trading else conn.paper_broker_simple,
161
167
  clock=self.clock,
162
168
  market_nxt_on=market_nxt_on,
169
+ strategy_name=strategy_name,
170
+ classifier_type=classifier_type,
163
171
  )
164
172
 
165
173
 
@@ -291,6 +291,7 @@ class KISSimpleDomesticStock:
291
291
  분봉 데이터 검색
292
292
 
293
293
  Note:
294
+ - 시간외 종가, 시간외 단일가 데이터는 포함되어 있지 않습니다.
294
295
  - 두 거래소에서 공통으로 거래정지된 종목의 시가/고가/저가/종가는 모두 동일하며, 그 외 값은 모두 0 입니다.
295
296
  - NXT 매매체결 종목이 아니거나, NXT 거래소에서 거래가 불가능한 종목을 DataExchange.NXT 또는 DataExchange.UN으로 조회하면, 모든 값은 0 입니다.
296
297
 
@@ -307,6 +308,10 @@ class KISSimpleDomesticStock:
307
308
  result = []
308
309
  schedule = get_market_schedule(dtm.date.today(), exchange="KRX" if data_exchange == DataExchange.KRX else "NXT")
309
310
 
311
+ # 만약 현재 시간이 거래소 마감 시간을 지났다면, 마감 시간으로 설정
312
+ if request_time.time() > schedule.close_time:
313
+ request_time = request_datetime.replace(hour=schedule.close_time.hour, minute=schedule.close_time.minute)
314
+
310
315
  while True:
311
316
  r = self.stock_api.inquire_time_itemchartprice(
312
317
  asset_code,
@@ -18,7 +18,7 @@ minuteCache = DiskCacheManager("minute_cache")
18
18
  @minuteCache.memoize()
19
19
  def get_all_minute_data(
20
20
  time: datetime.datetime,
21
- source: str = "ebest",
21
+ source: str = "kis",
22
22
  adjusted: bool = True,
23
23
  exchange: Union[str, DataExchange] = "KRX",
24
24
  ) -> pd.DataFrame:
@@ -31,7 +31,7 @@ def get_all_minute_data(
31
31
 
32
32
  Args:
33
33
  time (datetime.datetime): 조회할 시간
34
- source (str): 데이터를 검색할 API. 'ebest' 또는 'kis'를 지정할 수 있습니다. 기본값은 'ebest'입니다.
34
+ source (str): 데이터를 검색할 API. 'ebest' 또는 'kis'를 지정할 수 있습니다. 기본값은 'kis'입니다.
35
35
  adjusted (bool): 수정주가 여부. 기본값은 True.
36
36
  exchange (Union[str, DataExchange]): 거래소. 기본값은 KRX.
37
37
 
@@ -130,7 +130,7 @@ def get_all_day_data(
130
130
  date: datetime.date,
131
131
  codes: list[str] | str,
132
132
  period: datetime.timedelta = datetime.timedelta(minutes=1),
133
- source: str = "ebest",
133
+ source: str = "kis",
134
134
  adjusted: bool = True,
135
135
  ascending: bool = True,
136
136
  exchange: Union[str, DataExchange] = "KRX",
@@ -146,7 +146,7 @@ def get_all_day_data(
146
146
  date (datetime.date): 데이터를 검색할 날짜.
147
147
  codes (list[str]): 조회할 주식 코드들의 리스트. 최대 20개까지 지정할 수 있습니다.
148
148
  period (datetime.timedelta, optional): 반환된 데이터의 시간 간격. 기본값은 1분입니다. 30초 이상의 값을 30초간격으로 지정할 수 있습니다.
149
- source (str, optional): 데이터를 검색할 API. 'ebest' 또는 'kis'를 지정할 수 있습니다. 기본값은 'ebest'입니다.
149
+ source (str, optional): 데이터를 검색할 API. 'ebest' 또는 'kis'를 지정할 수 있습니다. 기본값은 'kis'입니다.
150
150
  adjusted (bool): 수정주가 여부. 기본값은 True.
151
151
  ascending (bool): 오름차순 여부. 기본값은 True.
152
152
  exchange (Union[str, DataExchange]): 거래소. 기본값은 KRX. (cf. NXT의 경우 해당되지 않는 종목은 Empty DataFrame이 반환됩니다.)
@@ -107,7 +107,7 @@ class PositionClassifier:
107
107
  self.logger.info(f"set_initial_position.\npositions: {cur_pos}\nauto_positions: {self.auto_positions}\ndirect_positions: {self.direct_positions}")
108
108
 
109
109
  def set_initial_order(self):
110
- cur_order = self.api.get_pending_orders()
110
+ cur_order = self.api.get_pending_orders(exchanges=[OrderExchange.KRX, OrderExchange.NXT, OrderExchange.SOR])
111
111
  kv_auto_orders = self.kv_store.get(self.DEF_AUTO_ORDER_KEY) or {}
112
112
  kv_direct_orders = self.kv_store.get(self.DEF_DIRECT_ORDER_KEY) or {}
113
113
  kv_auto_tag_orders = self.kv_store.get(self.DEF_TAG_AUTO_ORDER_KEY) or []
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