pyqqq 0.12.187__tar.gz → 0.12.189__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 (58) hide show
  1. {pyqqq-0.12.187 → pyqqq-0.12.189}/PKG-INFO +1 -1
  2. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyproject.toml +1 -1
  3. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/backtest/broker.py +93 -28
  4. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/kis/domestic_stock.py +54 -17
  5. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/kis/simple.py +2 -1
  6. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/data/daily.py +36 -24
  7. {pyqqq-0.12.187 → pyqqq-0.12.189}/README.md +0 -0
  8. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/__init__.py +0 -0
  9. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/backtest/__init__.py +0 -0
  10. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/backtest/environment.py +0 -0
  11. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/backtest/logger.py +0 -0
  12. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/backtest/positionprovider.py +0 -0
  13. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/backtest/strategy.py +0 -0
  14. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/backtest/utils.py +0 -0
  15. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/backtest/wallclock.py +0 -0
  16. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/__init__.py +0 -0
  17. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/ebest/__init__.py +0 -0
  18. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/ebest/domestic_stock.py +0 -0
  19. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/ebest/oauth.py +0 -0
  20. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/ebest/simple.py +0 -0
  21. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/ebest/tr_client.py +0 -0
  22. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/helper.py +0 -0
  23. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/kis/__init__.py +0 -0
  24. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/kis/oauth.py +0 -0
  25. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/kis/overseas_stock.py +0 -0
  26. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/kis/simple_overseas.py +0 -0
  27. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/kis/tr_client.py +0 -0
  28. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/multiprocess_tracker.py +0 -0
  29. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/brokerage/tracker.py +0 -0
  30. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/config.py +0 -0
  31. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/data/__init__.py +0 -0
  32. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/data/domestic.py +0 -0
  33. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/data/index.py +0 -0
  34. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/data/minutes.py +0 -0
  35. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/data/overseas.py +0 -0
  36. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/data/realtime.py +0 -0
  37. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/data/ticks.py +0 -0
  38. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/data/us_stocks.py +0 -0
  39. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/datatypes.py +0 -0
  40. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/executors/__init__.py +0 -0
  41. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/executors/hook.py +0 -0
  42. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/__init__.py +0 -0
  43. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/api_client.py +0 -0
  44. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/array.py +0 -0
  45. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/casting.py +0 -0
  46. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/compute.py +0 -0
  47. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/copycat.py +0 -0
  48. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/daily_tickers.py +0 -0
  49. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/display.py +0 -0
  50. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/kvstore.py +0 -0
  51. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/limiter.py +0 -0
  52. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/local_cache.py +0 -0
  53. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/logger.py +0 -0
  54. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/market_schedule.py +0 -0
  55. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/mock_api.py +0 -0
  56. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/position_classifier.py +0 -0
  57. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/retry.py +0 -0
  58. {pyqqq-0.12.187 → pyqqq-0.12.189}/pyqqq/utils/singleton.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyqqq
3
- Version: 0.12.187
3
+ Version: 0.12.189
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.187"
3
+ version = "0.12.189"
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"
@@ -287,8 +287,8 @@ class TradingBroker(BaseBroker):
287
287
  if data_exchange == DataExchange.NXT and price_data.get("open_price", 0) == 0: # current_price는 어제의 값을 반환하므로 open_price가 0인지로 NXT 시장 여부 확인
288
288
  price_data = self.data_api.get_price(code, data_exchange=DataExchange.KRX)
289
289
  else:
290
- if self.market_nxt_on: # TODO: 정규장에서도 NXT 가격을 우선 조회하는 것이니 수정 필요
291
- price_data = self.data_api.get_price(code, data_exchange=DataExchange.NXT)
290
+ if self.market_nxt_on:
291
+ price_data = self.data_api.get_price(code, data_exchange=DataExchange.UN)
292
292
  if price_data.get("open_price", 0) == 0: # current_price는 어제의 값을 반환하므로 open_price가 0인지로 NXT 시장 여부 확인
293
293
  price_data = self.data_api.get_price(code, data_exchange=DataExchange.KRX)
294
294
  else:
@@ -306,7 +306,7 @@ class TradingBroker(BaseBroker):
306
306
  result = self.data_api.get_today_minute_data(code, data_exchange=DataExchange.KRX)
307
307
  else:
308
308
  if self.market_nxt_on:
309
- result = self.data_api.get_today_minute_data(code, data_exchange=DataExchange.NXT)
309
+ result = self.data_api.get_today_minute_data(code, data_exchange=DataExchange.UN)
310
310
  if result.empty or result.iloc[-1].close == 0:
311
311
  result = self.data_api.get_today_minute_data(code, data_exchange=DataExchange.KRX)
312
312
  else:
@@ -316,12 +316,12 @@ class TradingBroker(BaseBroker):
316
316
  def get_daily_price(self, code: str, from_date: dtm.date, to_date: dtm.date, data_exchange: Optional[DataExchange] = None) -> pd.DataFrame:
317
317
  if data_exchange:
318
318
  result = self.data_api.get_historical_daily_data(code, from_date, to_date, adjusted_price=True, data_exchange=data_exchange)
319
- if data_exchange == DataExchange.NXT and result.empty:
319
+ if data_exchange == DataExchange.NXT and (result.empty or result.iloc[0].open == 0):
320
320
  result = self.data_api.get_historical_daily_data(code, from_date, to_date, adjusted_price=True, data_exchange=DataExchange.KRX)
321
321
  else:
322
322
  if self.market_nxt_on:
323
- result = self.data_api.get_historical_daily_data(code, from_date, to_date, adjusted_price=True, data_exchange=DataExchange.NXT)
324
- if result.empty:
323
+ result = self.data_api.get_historical_daily_data(code, from_date, to_date, adjusted_price=True, data_exchange=DataExchange.UN)
324
+ if result.empty or result.iloc[0].open == 0:
325
325
  result = self.data_api.get_historical_daily_data(code, from_date, to_date, adjusted_price=True, data_exchange=DataExchange.KRX)
326
326
  else:
327
327
  result = self.data_api.get_historical_daily_data(code, from_date, to_date, adjusted_price=True, data_exchange=DataExchange.KRX)
@@ -576,7 +576,12 @@ class MockBroker(BaseBroker):
576
576
  market_schedule = get_market_schedule(today, exchange=self._get_exchange_code())
577
577
  open_time = market_schedule.open_time
578
578
  if self.market_nxt_on:
579
- open_time = dtm.time(8, 0, 0)
579
+ try:
580
+ nxt_market_schedule = get_market_schedule(today, exchange="NXT")
581
+ open_time = nxt_market_schedule.open_time
582
+ except ValueError:
583
+ # 2025-03-04 이전 날짜는 ValueError 이므로 무시
584
+ pass
580
585
 
581
586
  if now.time() < open_time:
582
587
  last_trading_day = get_last_trading_day(today, exchange=self._get_exchange_code())
@@ -603,7 +608,7 @@ class MockBroker(BaseBroker):
603
608
 
604
609
  def get_minute_price(self, code: str) -> pd.DataFrame:
605
610
  """
606
- self.clock 기준 당일의 정규장 시작부터 현재 시각 (이전) 까지의 분봉 데이터를 조회합니다.
611
+ self.clock 기준 당일의 정규장 시작부터 현재 시각 (이전) 까지의 분봉 데이터를 조회합니다.
607
612
  단, market_nxt_on 이면, NXT 가능 종목의 경우 프리마켓, 정규마켓, 애프터마켓의 분봉을 모두 조회합니다.
608
613
 
609
614
  Args:
@@ -623,10 +628,15 @@ class MockBroker(BaseBroker):
623
628
  close_time = dtm.datetime.combine(today, market_schedule.close_time)
624
629
 
625
630
  # TODO: us_stock 에 대해서는 따로 적용해야 함.
626
- is_nxt = df.index[0] == dtm.datetime.combine(today, dtm.time(8, 0, 0))
627
- if self.market_nxt_on and is_nxt:
628
- open_time = dtm.datetime.combine(today, dtm.time(8, 0, 0))
629
- close_time = dtm.datetime.combine(today, dtm.time(20, 0, 0))
631
+ try:
632
+ nxt_market_schedule = get_market_schedule(today, "NXT")
633
+ is_nxt = df.index[0] == dtm.datetime.combine(today, nxt_market_schedule.open_time)
634
+ if self.market_nxt_on and is_nxt:
635
+ open_time = dtm.datetime.combine(today, nxt_market_schedule.open_time)
636
+ close_time = dtm.datetime.combine(today, nxt_market_schedule.close_time)
637
+ except ValueError:
638
+ # 2025-03-04 이전 날짜는 ValueError 이므로 무시
639
+ pass
630
640
 
631
641
  if df.index.tz is not None:
632
642
  df.index = df.index.tz_localize(None)
@@ -707,9 +717,40 @@ class MockBroker(BaseBroker):
707
717
  dfs = get_us_minute_data(date, [code])
708
718
  else:
709
719
  if self.market_nxt_on:
710
- dfs = get_kr_minute_data(date, [code], dtm.timedelta(minutes=1), source="kis", adjusted=True, exchange=DataExchange.NXT)
711
- if dfs[code].empty: # NXT 해당 종목이 없는 경우 KRX 의 결과값을 반환한다. 하지만 로컬 캐시 폴더는 NXT 기준으로 저장한다.
712
- dfs = get_kr_minute_data(date, [code], dtm.timedelta(minutes=1), source="kis", adjusted=True, exchange=DataExchange.KRX)
720
+ # NXT 해당 종목이 없는 경우 KRX 의 결과값을 반환한다. 하지만 로컬 캐시 폴더는 NXT 기준으로 저장한다.
721
+ dfs = get_kr_minute_data(date, [code], dtm.timedelta(minutes=1), source="kis", adjusted=True, exchange=DataExchange.KRX)
722
+ dfs_nxt = get_kr_minute_data(date, [code], dtm.timedelta(minutes=1), source="kis", adjusted=True, exchange=DataExchange.NXT)
723
+ if code in dfs_nxt and not dfs_nxt[code].empty:
724
+ _df = dfs[code]
725
+ _df_nxt = dfs_nxt[code]
726
+ common_dates = _df.index.intersection(_df_nxt.index)
727
+ for _date in common_dates:
728
+ _df_nxt.loc[_date, 'volume'] = _df.loc[_date, 'volume'] + _df_nxt.loc[_date, 'volume']
729
+ _df_nxt.loc[_date, 'value'] = _df.loc[_date, 'value'] + _df_nxt.loc[_date, 'value']
730
+ _df_nxt.loc[_date, 'cum_volume'] = _df.loc[_date, 'cum_volume'] + _df_nxt.loc[_date, 'cum_volume']
731
+ _df_nxt.loc[_date, 'cum_value'] = _df.loc[_date, 'cum_value'] + _df_nxt.loc[_date, 'cum_value']
732
+ # 한투 앱에서 통합 차트의 경우 KRX값이 우선으로 사용됨
733
+ _df_nxt.loc[_date, 'open'] = _df.loc[_date, 'open']
734
+ _df_nxt.loc[_date, 'high'] = max(_df.loc[_date, 'high'], _df_nxt.loc[_date, 'high'])
735
+ _df_nxt.loc[_date, 'low'] = min(_df.loc[_date, 'low'], _df_nxt.loc[_date, 'low'])
736
+ _df_nxt.loc[_date, 'close'] = _df.loc[_date, 'close']
737
+
738
+ # 15:30 이후의 cum_volume, cum_value는 15:30의 cum_volume, cum_value에다가 _df_nxt의 15:31 부터 각 volume, value를 매분 누적하여 저장
739
+ market_close_datetime = dtm.datetime.combine(date, get_market_schedule(date, "KRX").close_time)
740
+ if market_close_datetime in _df_nxt.index:
741
+ base_cum_volume = _df_nxt.loc[market_close_datetime, 'cum_volume']
742
+ base_cum_value = _df_nxt.loc[market_close_datetime, 'cum_value']
743
+
744
+ # 15:31 이후 데이터에 대해 누적 계산
745
+ after_market_close = _df_nxt.index > market_close_datetime
746
+ if after_market_close.any():
747
+ for idx in _df_nxt[after_market_close].index:
748
+ _df_nxt.loc[idx, 'cum_volume'] = base_cum_volume + _df_nxt.loc[idx, 'volume']
749
+ _df_nxt.loc[idx, 'cum_value'] = base_cum_value + _df_nxt.loc[idx, 'value']
750
+ base_cum_volume = _df_nxt.loc[idx, 'cum_volume']
751
+ base_cum_value = _df_nxt.loc[idx, 'cum_value']
752
+
753
+ dfs[code] = _df_nxt
713
754
  else:
714
755
  dfs = get_kr_minute_data(date, [code], dtm.timedelta(minutes=1), source="kis", adjusted=True, exchange=DataExchange.KRX)
715
756
 
@@ -736,7 +777,7 @@ class MockBroker(BaseBroker):
736
777
 
737
778
  def get_daily_price(self, code: str, from_date: Optional[dtm.date] = None, end_date: Optional[dtm.date] = None):
738
779
  """
739
- 지정된 기간 동안의 정규장 시작부터 현재 시각 (이전) 까지의 일봉 데이터를 조회합니다.
780
+ 지정된 기간 동안의 정규장 시작부터 현재 시각 (이전) 까지의 일봉 데이터를 조회합니다.
740
781
  단, market_nxt_on 이면, NXT 가능 종목의 경우 프리마켓, 정규마켓, 애프터마켓의 일봉을 모두 조회합니다.
741
782
  """
742
783
  today = self.clock.today()
@@ -758,9 +799,18 @@ class MockBroker(BaseBroker):
758
799
  dfs = get_us_daily_data([code], from_date, end_date, adjusted=True, ascending=True)
759
800
  else:
760
801
  if self.market_nxt_on:
761
- dfs = get_kr_daily_data([code], from_date, end_date, adjusted=True, ascending=True, exchange=DataExchange.NXT)
762
- if code not in dfs or dfs[code].empty:
763
- dfs = get_kr_daily_data([code], from_date, end_date, adjusted=True, ascending=True, exchange=DataExchange.KRX)
802
+ dfs = get_kr_daily_data([code], from_date, end_date, adjusted=True, ascending=True, exchange=DataExchange.KRX)
803
+ dfs_nxt = get_kr_daily_data([code], from_date, end_date, adjusted=True, ascending=True, exchange=DataExchange.NXT)
804
+ if code in dfs_nxt:
805
+ _df = dfs[code]
806
+ _df_nxt = dfs_nxt[code]
807
+
808
+ # 같은 날짜의 데이터만 volume과 value를 합치고, open, close는 NXT 값으로 교체
809
+ common_dates = _df.index.intersection(_df_nxt.index)
810
+ for _date in common_dates:
811
+ _df.loc[_date, 'volume'] = _df.loc[_date, 'volume'] + _df_nxt.loc[_date, 'volume']
812
+ _df.loc[_date, 'value'] = _df.loc[_date, 'value'] + _df_nxt.loc[_date, 'value']
813
+ _df.loc[_date, 'open'] = _df_nxt.loc[_date, 'open']
764
814
  else:
765
815
  dfs = get_kr_daily_data([code], from_date, end_date, adjusted=True, ascending=True, exchange=DataExchange.KRX)
766
816
 
@@ -773,8 +823,13 @@ class MockBroker(BaseBroker):
773
823
  open_time = schedule.open_time
774
824
  close_time = schedule.close_time
775
825
  if self.market_nxt_on:
776
- open_time = dtm.time(8, 0, 0)
777
- close_time = dtm.time(20, 0, 0)
826
+ try:
827
+ nxt_schedule = get_market_schedule(today, "NXT")
828
+ open_time = nxt_schedule.open_time
829
+ close_time = nxt_schedule.close_time
830
+ except ValueError:
831
+ # 2025-03-04 이전 날짜는 ValueError 이므로 무시
832
+ pass
778
833
 
779
834
  if schedule.full_day_closed:
780
835
  pass # end_date 가 폐장일이면 해당 날짜의 데이터는 이미 없음
@@ -933,18 +988,22 @@ class MockBroker(BaseBroker):
933
988
  self.get_positions() # self.positions 에 있는 포지션들의 현재 가격을 조회하여 업데이트 함
934
989
 
935
990
  today = current_time.date()
936
- market_schedule = get_market_schedule(today)
991
+ market_schedule = get_market_schedule(today, exchange=Exchange.KRX)
937
992
 
938
993
  open_time = dtm.datetime.combine(today, market_schedule.open_time)
939
994
  close_time = dtm.datetime.combine(today, market_schedule.close_time)
940
995
  preclose_auction_start_time = close_time - dtm.timedelta(minutes=10)
941
996
 
942
- nxt_pre_open_time = dtm.datetime.combine(today, dtm.time(8, 0, 0))
943
- nxt_pre_close_time = dtm.datetime.combine(today, dtm.time(8, 50, 0))
944
- # nxt_main_open_time = dtm.datetime.combine(today, dtm.time(9, 0, 30))
945
- # nxt_main_close_time = dtm.datetime.combine(today, dtm.time(15, 20, 0))
946
- nxt_after_open_time = dtm.datetime.combine(today, dtm.time(15, 40, 0))
947
- nxt_after_close_time = dtm.datetime.combine(today, dtm.time(20, 0, 0))
997
+ try:
998
+ nxt_market_schedule = get_market_schedule(today, exchange=Exchange.NXT)
999
+ except ValueError:
1000
+ nxt_market_schedule = market_schedule
1001
+ nxt_pre_open_time = dtm.datetime.combine(today, nxt_market_schedule.open_time)
1002
+ nxt_pre_close_time = nxt_pre_open_time + dtm.timedelta(minutes=50)
1003
+ # nxt_main_open_time = dtm.datetime.combine(today, open_time) + dtm.timedelta(seconds=30)
1004
+ # nxt_main_close_time = close_time - dtm.timedelta(minutes=10)
1005
+ nxt_after_open_time = close_time
1006
+ nxt_after_close_time = dtm.datetime.combine(today, nxt_market_schedule.close_time)
948
1007
 
949
1008
  just_closed = current_time.replace(second=0, microsecond=0) == close_time.replace(second=0, microsecond=0)
950
1009
  if preclose_auction_start_time - dtm.timedelta(minutes=1) <= current_time <= preclose_auction_start_time + dtm.timedelta(minutes=1):
@@ -971,6 +1030,10 @@ class MockBroker(BaseBroker):
971
1030
  # 강제로 1분 차이를 만들어 줌
972
1031
  before_time = current_time - dtm.timedelta(seconds=60)
973
1032
 
1033
+ if self.market_nxt_on and current_time < nxt_after_close_time and current_time - before_time < dtm.timedelta(seconds=60):
1034
+ # 강제로 1분 차이를 만들어 줌
1035
+ before_time = current_time - dtm.timedelta(seconds=60)
1036
+
974
1037
  df = self.get_minute_price(order.asset_code)
975
1038
  if df.empty:
976
1039
  self.logger.warning(f"ORDER CANCELED: {order.asset_code} minute data is empty")
@@ -1278,6 +1341,7 @@ class MockBroker(BaseBroker):
1278
1341
  d["market_cap"] = round(dft["value"].iloc[-1] / 100000000)
1279
1342
  except Exception as e:
1280
1343
  self.logger.error(f"get_market_cap error: {d['asset_code']} {d['name']} {prev_date} {e}")
1344
+ d["market_cap"] = 0
1281
1345
 
1282
1346
  # 전일 거래대금
1283
1347
  try:
@@ -1285,6 +1349,7 @@ class MockBroker(BaseBroker):
1285
1349
  d["prev_value"] = round(dft["value"].iloc[-1] / 100000000)
1286
1350
  except Exception as e:
1287
1351
  self.logger.error(f"전일거래대금 error: {d['asset_code']} {d['name']} {prev_date} {e}")
1352
+ d["prev_value"] = 0
1288
1353
  dict_list.append(d)
1289
1354
 
1290
1355
  df = pd.DataFrame(dict_list)
@@ -122,6 +122,7 @@ class KISDomesticStock:
122
122
  - mrkt_warn_cls_code: 시장경고코드 - 00:없음 01:투자주의 02:투자경고 03:투자위험
123
123
  - short_over_yn: 단기과열여부 - Y/N
124
124
  - sltr_yn: 정리매매여부 - Y/N
125
+ - mang_issu_cls_code: 관리종목여부 - Y/N
125
126
 
126
127
  Raise:
127
128
  ValueError: API 에러 발생시
@@ -666,7 +667,7 @@ class KISDomesticStock:
666
667
  - per: PER
667
668
  - eps: EPS
668
669
  - pbr: PBR
669
- - itewhol_loan_rmnd_ratem: name 전체 융자 잔고 비율
670
+ - itewhol_loan_rmnd_ratem: 전체 융자 잔고 비율
670
671
 
671
672
  - output2: (list): 일별데이터
672
673
 
@@ -688,12 +689,23 @@ class KISDomesticStock:
688
689
  | 06:권리중간배당락
689
690
  | 07:권리분기배당락
690
691
 
691
- - prtt_rate: 분할 비율
692
- - mod_yn: 분할변경여부
692
+ - prtt_rate: 분할 비율 - 기준가/전일 종가
693
+ - mod_yn: 분할변경여부 - 현재 영업일에 체결이 발생하지 않아 시가가 없을경우 Y 로 표시(차트에서 사용)
693
694
  - prdy_vrss_sign: 전일 대비 부호
694
695
  - prdy_vrss: 전일 대비
695
696
  - revl_issu_reas: 재평가사유코드
696
697
 
698
+ | 00:해당없음
699
+ | 01:회사분할
700
+ | 02:자본감소
701
+ | 03:장기간정지
702
+ | 04:초과분배
703
+ | 05:대규모배당
704
+ | 06:회사분할합병
705
+ | 07:ETN증권병합/분할
706
+ | 08:신종증권기세조정
707
+ | 99:기타
708
+
697
709
  Raise:
698
710
  ValueError: API 에러 발생시
699
711
  """
@@ -1603,7 +1615,7 @@ class KISDomesticStock:
1603
1615
 
1604
1616
  return result
1605
1617
 
1606
- def get_program_trade_by_stock(self, fid_input_iscd: str, tr_cont: str = ""):
1618
+ def get_program_trade_by_stock(self, fid_input_iscd: str, tr_cont: str = "", fid_cond_mrkt_div_code: str = "J"):
1607
1619
  """
1608
1620
  (국내주식시세) 종목별 프로그램매매추이(체결)[v1_국내주식-044]
1609
1621
 
@@ -1614,6 +1626,7 @@ class KISDomesticStock:
1614
1626
  Args:
1615
1627
  fid_input_iscd (str): 입력 종목코드 - 종목번호(6자리) ETN의 경우 Q로 시작(EX. Q500001)
1616
1628
  tr_cont (str): 연속조회여부
1629
+ fid_cond_mrkt_div_code (str): FID 조건시장분류코드 - J:주식,ETF,ETN NX:Nextrade UN:통합
1617
1630
 
1618
1631
  Returns:
1619
1632
  dict:
@@ -1647,7 +1660,10 @@ class KISDomesticStock:
1647
1660
  url_path = "/uapi/domestic-stock/v1/quotations/program-trade-by-stock"
1648
1661
  tr_id = "FHPPG04650101"
1649
1662
 
1650
- params = {"FID_INPUT_ISCD": fid_input_iscd, "FID_COND_MRKT_DIV_CODE": "UN"}
1663
+ params = {
1664
+ "FID_INPUT_ISCD": fid_input_iscd,
1665
+ "FID_COND_MRKT_DIV_CODE": fid_cond_mrkt_div_code,
1666
+ }
1651
1667
 
1652
1668
  res_body, res_headers = self._tr_request(url_path, tr_id, tr_cont, params=params)
1653
1669
  if res_body["rt_cd"] != "0":
@@ -1674,7 +1690,17 @@ class KISDomesticStock:
1674
1690
 
1675
1691
  return result
1676
1692
 
1677
- def get_volume_rank(self, fid_input_iscd: str, fid_div_cls_code: str, fid_blng_cls_code: str, fid_trgt_cls_code: str, fid_trgt_excls_cls_code: str, fid_input_price_1: int = None, fid_input_price_2: int = None, fid_vol_cnt: int = None):
1693
+ def get_volume_rank(
1694
+ self,
1695
+ fid_input_iscd: str,
1696
+ fid_div_cls_code: str,
1697
+ fid_blng_cls_code: str,
1698
+ fid_trgt_cls_code: str,
1699
+ fid_trgt_excls_cls_code: str,
1700
+ fid_input_price_1: int = None,
1701
+ fid_input_price_2: int = None,
1702
+ fid_vol_cnt: int = None,
1703
+ ):
1678
1704
  """
1679
1705
  (국내주식시세) 거래량순위[v1_국내주식-047]
1680
1706
 
@@ -2710,8 +2736,8 @@ class KISDomesticStock:
2710
2736
  - fix_lblt (str): 고정부채
2711
2737
  - total_lblt (str): 부채총계
2712
2738
  - cpfn (str): 자본금
2713
- - cfp_surp (str): 자본 잉여금
2714
- - prfi_surp (str): 이익 잉여금
2739
+ - cfp_surp (str): 자본 잉여금 - 출력되지 않는 데이터(99.99로 표시)
2740
+ - prfi_surp (str): 이익 잉여금 - 출력되지 않는 데이터(99.99로 표시)
2715
2741
  - total_cptl (str): 자본총계
2716
2742
 
2717
2743
  Raise:
@@ -2874,8 +2900,11 @@ class KISDomesticStock:
2874
2900
  Args:
2875
2901
  gb1 (str): 조회구분 - 0: 배당전체, 1: 결산배당, 2: 중간배당
2876
2902
  f_dt (str): 조회일자From - 일자 ~
2877
- t_dt (str): 종목코드To - ~ 일자
2903
+ t_dt (str): 조회일자To - ~ 일자
2878
2904
  sht_cd (str): 종목코드 - 공백: 전체, 특정종목 조회시 : 종목코드
2905
+ high_gb (str): 고배당여부 - 공백
2906
+ cts (str): CTS - 공백
2907
+
2879
2908
 
2880
2909
  Returns:
2881
2910
  dict:
@@ -3222,14 +3251,14 @@ class KISDomesticStock:
3222
3251
 
3223
3252
  | - 신용매수
3224
3253
  | - 21:자기융자신규
3225
- | - 23:유동융자신규
3226
- | - 26:유동대주상환
3254
+ | - 23:유통융자신규
3255
+ | - 26:유통대주상환
3227
3256
  | - 28:자기대주상환
3228
3257
  | - 신용매도
3229
- | - 25:자기융자상환
3230
- | - 27:유동융자상환
3231
- | - 22:유동대주신규
3258
+ | - 22:유통대주신규
3232
3259
  | - 24:자기대주신규
3260
+ | - 25:자기융자상환
3261
+ | - 27:유통융자상환
3233
3262
 
3234
3263
  load_dt (dtm.date): 대출일자
3235
3264
 
@@ -3477,7 +3506,7 @@ class KISDomesticStock:
3477
3506
  self,
3478
3507
  cano: str,
3479
3508
  acnt_prdt_cd: str,
3480
- inqr_dvsn_1: str = "1",
3509
+ inqr_dvsn_1: str = "0",
3481
3510
  inqr_dvsn_2: str = "0",
3482
3511
  ctx_area_fk100: str = "",
3483
3512
  ctx_area_nk100: str = "",
@@ -3490,7 +3519,7 @@ class KISDomesticStock:
3490
3519
  Args:
3491
3520
  cano (str): 종합계좌번호 - 계좌번호 체계(8-2)의 앞 8자리
3492
3521
  acnt_prdt_cd (str): 계좌상품코드 - 계좌번호 체계(8-2)의 뒤 2자리
3493
- inqr_dvsn_1 (str): 조회구분1 - 0:조회순서 1:주문순 2:종목순
3522
+ inqr_dvsn_1 (str): 조회구분1 - 0:주문 1:종목
3494
3523
  inqr_dvsn_2 (str): 조회구분2 - 0:전체 1:매도 2:매수
3495
3524
  ctx_area_fk100 (str): CTX_AREA_FK100
3496
3525
  ctx_area_nk100 (str): CTX_AREA_NK100
@@ -3659,8 +3688,9 @@ class KISDomesticStock:
3659
3688
  sll_buy_dvsn_cd (str): 매도매수구분코드 - 00:전체 01:매도 02:매수
3660
3689
  inqr_dvsn (str): 조회구분 - 00:역순 01:정순
3661
3690
  pdno (str): 상품번호 - 종목번호(6자리) 공란:전체 조회
3691
+ odno (str): 주문번호 - 주문시 한국투자증권 시스템에서 채번된 주문번호
3662
3692
  ccld_dvsn (str): 체결구분 - 00:전체 01:체결 02:미체결
3663
- inqr_dvsn_3 (str): 조회구분3 - 00:전체 01:현금 02:융자 03:대출 04:대주 05:대여 06:자기융자신규/상환 07:유통융자신규/상환
3693
+ inqr_dvsn_3 (str): 조회구분3 - 00:전체 01:현금 02:신용 03:담보 04:대주 05:대여 06:자기융자신규/상환 07:유통융자신규/상환
3664
3694
  inqr_dvsn_1 (str): 조회구분1 - 공란:전체 1:ELW 2:프리보드
3665
3695
  ctx_area_fk100 (str): 연속조회검색조건100 - "":최초 조회시 이전 조회 ctx_area_fk100 값:다음페이지 조회시(2번째부터)
3666
3696
  ctx_area_nk100 (str): 연속조회키100 - "":최초 조회시 이전 조회 ctx_area_nk100 값:다음페이지 조회시(2번째부터)
@@ -3697,6 +3727,7 @@ class KISDomesticStock:
3697
3727
  - cncl_yn: 취소여부
3698
3728
  - tot_ccld_amt: 총체결금액
3699
3729
  - loan_dt: 대출일자
3730
+ - ordr_empno: 주문자사번
3700
3731
  - ord_dvsn_cd: 주문구분코드
3701
3732
 
3702
3733
  | 00:지정가
@@ -3720,6 +3751,9 @@ class KISDomesticStock:
3720
3751
  - rmn_qty: 잔여수량
3721
3752
  - rjct_qty: 거부수량
3722
3753
  - ccld_cndt_name: 체결조건명
3754
+ - inqr_ip_addr: 조회IP주소
3755
+ - cpbc_ordp_ord_rcit_dvsn_cd: 전산주문표주문접수구분코드
3756
+ - cpbc_ordp_infm_mthd_dvsn_cd: 전산주문표통보방법구분코드
3723
3757
  - infm_tmd: 통보시각 - ※ 실전투자계좌로는 해당값이 제공되지 않습니다.
3724
3758
  - ctac_tlno: 연락전화번호
3725
3759
  - prdt_type_cd: 상품유형코드 - 300:주식 301:선물옵션 302:채권 306:ELS
@@ -4644,6 +4678,9 @@ class KISDomesticStock:
4644
4678
 
4645
4679
  한국투자 HTS(eFriend Plus) > [0856] 기간별 매매손익 화면 에서 "일별" 클릭 시의 기능을 API로 개발한 사항으로, 해당 화면을 참고하시면 기능을 이해하기 쉽습니다.
4646
4680
 
4681
+ Note:
4682
+ - 00:30 ~ 03:00 사이에는 조회 불가능합니다.
4683
+
4647
4684
  Args:
4648
4685
  acnt_prdt_cd (str): 계좌상품코드
4649
4686
  cano (str): 종합계좌번호
@@ -1107,7 +1107,8 @@ class KISSimpleDomesticStock:
1107
1107
  elif ord_exg_gb == "4":
1108
1108
  exchange = OrderExchange.SOR
1109
1109
 
1110
- print(f"- rctf_cls={rctf_cls} acpt_yn={acpt_yn} cntg_yn={cntg_yn} accepted={accepted} executed={executed} rejected={rejected} status={event_type}")
1110
+ if account_no == f"{self.account_no}{self.account_product_code}":
1111
+ print(f"- rctf_cls={rctf_cls} acpt_yn={acpt_yn} cntg_yn={cntg_yn} accepted={accepted} executed={executed} rejected={rejected} status={event_type}")
1111
1112
 
1112
1113
  return OrderEvent(
1113
1114
  asset_code,
@@ -46,23 +46,26 @@ def get_all_ohlcv_for_date(
46
46
  - close (int): 종가.
47
47
  - volume (int): 거래량.
48
48
  - value (int): 거래대금.
49
- - diff (int): 종가 대비 전일 종가의 차이.
50
- - diff_rate (float): 종가 대비 전일 종가의 변화율.
51
49
 
52
50
  Raises:
53
51
  HTTPError: API 요청이 실패했을 때 발생.
54
52
 
55
53
  Examples:
56
- >>> ohlcv_data = get_all_ohlcv_for_date(datetime.date(2023, 5, 8))
54
+ >>> ohlcv_data = get_all_ohlcv_for_date(datetime.date(2025, 3, 4), exchange="KRX")
57
55
  >>> print(ohlcv_data)
58
- open high low close volume value diff diff_rate
56
+ open high low close volume value
59
57
  code
60
- 000020 8710 8790 8710 8770 39019 341233350 60 0.69
61
- 000040 1052 1133 1047 1047 590401 632158688 8 0.77
62
- 000050 7740 7870 7700 7750 1445 11211730 10 0.13
63
- 000070 68300 68800 67400 67600 33358 2261622200 -800 -1.17
64
- 000075 54800 54900 54800 54800 177 9702400 -100 -0.18
65
- ...
58
+ 000020 6200 6220 6090 6130 41954 257785065
59
+ 000040 422 422 393 400 135979 54618038
60
+ 000050 6350 6350 6240 6290 9957 62588010
61
+ 000070 59800 61100 59500 60400 32555 1957081250
62
+ 000075 59000 59400 58300 59400 2600 151987900
63
+ ... ... ... ... ... ... ...
64
+ 950160 35500 39150 35500 38950 332462 12638215825
65
+ 950170 4900 4955 4880 4900 24198 118688025
66
+ 950190 7860 7920 7770 7860 21309 166408215
67
+ 950200 3855 3855 3785 3800 23268 88458215
68
+ 950220 996 996 945 954 440017 421451605
66
69
  """
67
70
  if isinstance(date, datetime.datetime):
68
71
  date = date.date()
@@ -89,10 +92,15 @@ def get_all_ohlcv_for_date(
89
92
  return pd.DataFrame()
90
93
  else:
91
94
  df = pd.DataFrame(rows, columns=cols)
92
- df = df.astype({"open": int, "high": int, "low": int, "close": int, "volume": int, "value": int, "diff": int, "diff_rate": float})
93
- df.drop(columns=["date"], inplace=True)
95
+
96
+ # NOTE 서버 응답에서 diff, diff_rate 가 제거될 예정
97
+ columns_to_drop = ["diff", "diff_rate", "date"]
98
+ df.drop(columns=[col for col in columns_to_drop if col in df.columns], inplace=True)
94
99
  df.set_index("code", inplace=True)
95
100
 
101
+ df = df[["open", "high", "low", "close", "volume", "value"]]
102
+ df = df.astype({"open": int, "high": int, "low": int, "close": int, "volume": int, "value": int})
103
+
96
104
  return df
97
105
 
98
106
 
@@ -133,25 +141,23 @@ def get_ohlcv_by_codes_for_period(
133
141
  - close (int): 종가.
134
142
  - volume (int): 거래량.
135
143
  - value (int): 거래대금.
136
- - diff (int): 종가 대비 전일 종가의 차이.
137
- - diff_rate (float): 종가 대비 전일 종가의 변화율.
138
144
 
139
145
  Raises:
140
146
  HTTPError: API 요청이 실패했을 때 발생.
141
147
 
142
148
  Examples:
143
- >>> dfs = get_ohlcv_by_codes_for_period(['005930', '319640'], datetime.date(2024, 5, 7), datetime.date(2024, 5, 9))
149
+ >>> dfs = get_ohlcv_by_codes_for_period(['005930', '319640'], datetime.date(2025, 7, 28), datetime.date(2025, 7, 30))
144
150
  >>> print(dfs)
145
- {'319640': open high low close volume value diff diff_rate
151
+ {'319640': open high low close volume value
146
152
  date
147
- 2024-05-09 15380 15445 15365 15430 16200 249577480 -5 -0.03
148
- 2024-05-08 15445 15460 15365 15435 6419 99007660 -5 -0.03
149
- 2024-05-07 15355 15525 15355 15440 22318 345280470 85 0.55,
150
- '005930': open high low close volume value diff diff_rate
153
+ 2025-07-30 20860 20930 20840 20870 47431 990834244
154
+ 2025-07-29 20795 20850 20765 20810 39627 824529926
155
+ 2025-07-28 20900 20985 20880 20965 26610 556764707,
156
+ '005930': open high low close volume value
151
157
  date
152
- 2024-05-09 81100 81500 79700 79700 18700919 1504404274500 -1600 -1.97
153
- 2024-05-08 80800 81400 80500 81300 12960682 1050108654400 0 0.00
154
- 2024-05-07 79600 81300 79400 81300 26238868 2112619288066 3700 4.77}
158
+ 2025-07-30 71000 73700 70600 72600 34761444 2521446314798
159
+ 2025-07-29 70800 70800 68800 70600 28190940 1977721049950
160
+ 2025-07-28 68200 70400 67200 70400 35332500 2431396606750}
155
161
  """
156
162
  exchange = DataExchange.validate(exchange)
157
163
  tz = pytz.timezone("Asia/Seoul")
@@ -192,8 +198,14 @@ def get_ohlcv_by_codes_for_period(
192
198
  rows.reverse()
193
199
 
194
200
  df = pd.DataFrame(rows, columns=cols)
195
- df = df.astype({"open": int, "high": int, "low": int, "close": int, "volume": int, "value": int, "diff": int, "diff_rate": float})
201
+
202
+ # NOTE 서버 응답에서 diff, diff_rate 가 제거될 예정
203
+ columns_to_drop = ["diff", "diff_rate"]
204
+ df.drop(columns=[col for col in columns_to_drop if col in df.columns], inplace=True)
196
205
  df.set_index("date", inplace=True)
206
+
207
+ df = df[["open", "high", "low", "close", "volume", "value"]]
208
+ df = df.astype({"open": int, "high": int, "low": int, "close": int, "volume": int, "value": int})
197
209
  df.sort_index(ascending=ascending, inplace=True)
198
210
  result[code] = df
199
211
 
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