pyqqq 0.12.187__tar.gz → 0.12.188__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.
- {pyqqq-0.12.187 → pyqqq-0.12.188}/PKG-INFO +1 -1
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyproject.toml +1 -1
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/backtest/broker.py +93 -28
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/kis/domestic_stock.py +5 -2
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/data/daily.py +36 -24
- {pyqqq-0.12.187 → pyqqq-0.12.188}/README.md +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/__init__.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/backtest/__init__.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/backtest/environment.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/backtest/logger.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/backtest/positionprovider.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/backtest/strategy.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/backtest/utils.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/backtest/wallclock.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/__init__.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/ebest/__init__.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/ebest/domestic_stock.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/ebest/oauth.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/ebest/simple.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/ebest/tr_client.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/helper.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/kis/__init__.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/kis/oauth.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/kis/overseas_stock.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/kis/simple.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/kis/simple_overseas.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/kis/tr_client.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/multiprocess_tracker.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/brokerage/tracker.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/config.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/data/__init__.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/data/domestic.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/data/index.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/data/minutes.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/data/overseas.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/data/realtime.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/data/ticks.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/data/us_stocks.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/datatypes.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/executors/__init__.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/executors/hook.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/__init__.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/api_client.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/array.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/casting.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/compute.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/copycat.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/daily_tickers.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/display.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/kvstore.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/limiter.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/local_cache.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/logger.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/market_schedule.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/mock_api.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/position_classifier.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/retry.py +0 -0
- {pyqqq-0.12.187 → pyqqq-0.12.188}/pyqqq/utils/singleton.py +0 -0
|
@@ -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:
|
|
291
|
-
price_data = self.data_api.get_price(code, data_exchange=DataExchange.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
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
|
-
|
|
711
|
-
|
|
712
|
-
|
|
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.
|
|
762
|
-
|
|
763
|
-
|
|
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
|
-
|
|
777
|
-
|
|
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
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
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)
|
|
@@ -3477,7 +3477,7 @@ class KISDomesticStock:
|
|
|
3477
3477
|
self,
|
|
3478
3478
|
cano: str,
|
|
3479
3479
|
acnt_prdt_cd: str,
|
|
3480
|
-
inqr_dvsn_1: str = "
|
|
3480
|
+
inqr_dvsn_1: str = "0",
|
|
3481
3481
|
inqr_dvsn_2: str = "0",
|
|
3482
3482
|
ctx_area_fk100: str = "",
|
|
3483
3483
|
ctx_area_nk100: str = "",
|
|
@@ -3490,7 +3490,7 @@ class KISDomesticStock:
|
|
|
3490
3490
|
Args:
|
|
3491
3491
|
cano (str): 종합계좌번호 - 계좌번호 체계(8-2)의 앞 8자리
|
|
3492
3492
|
acnt_prdt_cd (str): 계좌상품코드 - 계좌번호 체계(8-2)의 뒤 2자리
|
|
3493
|
-
inqr_dvsn_1 (str): 조회구분1 - 0
|
|
3493
|
+
inqr_dvsn_1 (str): 조회구분1 - 0:주문 1:종목
|
|
3494
3494
|
inqr_dvsn_2 (str): 조회구분2 - 0:전체 1:매도 2:매수
|
|
3495
3495
|
ctx_area_fk100 (str): CTX_AREA_FK100
|
|
3496
3496
|
ctx_area_nk100 (str): CTX_AREA_NK100
|
|
@@ -4644,6 +4644,9 @@ class KISDomesticStock:
|
|
|
4644
4644
|
|
|
4645
4645
|
한국투자 HTS(eFriend Plus) > [0856] 기간별 매매손익 화면 에서 "일별" 클릭 시의 기능을 API로 개발한 사항으로, 해당 화면을 참고하시면 기능을 이해하기 쉽습니다.
|
|
4646
4646
|
|
|
4647
|
+
Note:
|
|
4648
|
+
- 00:30 ~ 03:00 사이에는 조회 불가능합니다.
|
|
4649
|
+
|
|
4647
4650
|
Args:
|
|
4648
4651
|
acnt_prdt_cd (str): 계좌상품코드
|
|
4649
4652
|
cano (str): 종합계좌번호
|
|
@@ -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(
|
|
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
|
|
56
|
+
open high low close volume value
|
|
59
57
|
code
|
|
60
|
-
000020
|
|
61
|
-
000040
|
|
62
|
-
000050
|
|
63
|
-
000070
|
|
64
|
-
000075
|
|
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
|
-
|
|
93
|
-
|
|
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(
|
|
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
|
|
151
|
+
{'319640': open high low close volume value
|
|
146
152
|
date
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
'005930': open high low close volume value
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|