elbakramer / koapy

KOAPY 는 키움 OpenAPI 를 Python 에서 쉽게 사용할 수 있도록 만든 라이브러리 패키지 및 툴입니다.
https://koapy.readthedocs.io
MIT License
199 stars 78 forks source link

조건검색 오류 #104

Open gbmax opened 1 year ago

gbmax commented 1 year ago

안녕하세요. 오늘 처음 설치하고 시험해보는데 잘 만들어진 프로그램이고 안정화되면 좋을 것 같아요. main_scenario.py 를 실행해보고 있는데 '5. 조건검색 예시'에서 오류가 발생합니다. 추적해보니 계정에서 등록한 조건검색은 잘 가져왔는데 다음과 같은 메시지로 오류 발생합니다.

======================================================================== 2023-01-02 18:26:47,383 [INFO] Getting stock codes with condition: 최근장대양봉 - :12 Output exceeds the size limit. Open the full output data in a text editor

_MultiThreadedRendezvous Traceback (most recent call last) d:\0.work\program\secu\koapy\koapy\examples\06_main_scenario.py in line 13 113 # condition_name = "대형 저평가 우량주" 115 logging.info("Getting stock codes with condition: %s", condition_name) ---> 116 codes, info = entrypoint.GetCodeListByCondition(condition_name, with_info=True) 118 print(codes) 119 print(info)

File D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\grpc\KiwoomOpenApiPlusServiceClientStubWrapper.py:1298, in KiwoomOpenApiPlusServiceClientStubExtendedWrapper.GetCodeListByCondition(self, condition_name, condition_index, with_info, is_future_option, request_name, screen_no) 1295 records = [] 1296 remove_zeros_width = None -> 1298 for response in self.ConditionCall( 1299 screen_no, 1300 condition_name, 1301 condition_index, 1302 search_type, 1303 with_info, 1304 is_future_option, 1305 request_name, 1306 ): 1307 if response.name == "OnReceiveTrCondition": 1308 code_list = response.arguments[1].string_value

File d:\miniconda3\envs\koapy\lib\site-packages\grpc_channel.py:426, in _Rendezvous.next(self) ... _MultiThreadedRendezvous: <_MultiThreadedRendezvous of RPC that terminated with: status = StatusCode.UNKNOWN details = "Exception iterating responses: 'str' object has no attribute 'name'" debug_error_string = "UNKNOWN:Error received from peer ipv4:127.0.0.1:5943 {grpc_message:"Exception iterating responses: \'str\' object has no attribute \'name\'", grpc_status:2, created_time:"2023-01-02T09:30:07.704446572+00:00"}"

gbmax commented 1 year ago

10_condition.py 실행시에도 비슷한 오류이나 메시지가 조금 다른 듯하네요.

vscode 상에서 KiwoomOpenApiPlusServiceClientStubWrapper.py 파일의 for response in self.ConditionCall()에서 오류나면서 표시되는 메시지

Exception has occurred: _MultiThreadedRendezvous <_MultiThreadedRendezvous of RPC that terminated with: status = StatusCode.UNKNOWN details = "Exception iterating responses: 'str' object has no attribute 'name'" debug_error_string = "UNKNOWN:Error received from peer ipv6:%5B::1%5D:5943 {created_time:"2023-01-02T09:58:09.62112417+00:00", grpc_status:2, grpc_message:"Exception iterating responses: \'str\' object has no attribute \'name\'"}"

================================================ 터미널 콘솔에 표시되는 메시지

2023-01-02 18:55:27,329 [DEBUG] OnEventConnect(0) - KiwoomOpenApiPlusLoggingEventHandler.py:89 2023-01-02 18:55:30,748 [DEBUG] OnReceiveConditionVer(1, '[OK] 사용자 조건검색식 읽기') - KiwoomOpenApiPlusLoggingEventHandler.py:115 [(0, '어제상승'), (1, '최근장대양봉'), (2, '골든크로스')] 2023-01-02 18:58:09,601 [ERROR] Exception while running QRateLimitedExecutorRunnable - QRateLimitedExecutor.py:46Traceback (most recent call last): File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\utils\pyside2\QRateLimitedExecutor.py", line 44, in run result = self._fn(*self._args, self._kwargs) File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\utils\pyside2\QRateLimitedExecutor.py", line 95, in _queuedCallFn self._checkAndSleepIfNecessary(*args, *kwargs) File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\utils\pyside2\QRateLimitedExecutor.py", line 76, in _checkAndSleepIfNecessary sleep_seconds = self._limiter.check_sleep_seconds(args, kwargs) File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\core\KiwoomOpenApiPlusRateLimiter.py", line 71, in check_sleep_seconds if fn.name == "SendCondition": AttributeError: 'str' object has no attribute 'name' ERROR:concurrent.futures:exception calling callback for <Future at 0x10f27370 state=finished raised AttributeError> Traceback (most recent call last): File "D:\miniconda3\envs\koapy\lib\concurrent\futures_base.py", line 328, in _invoke_callbacks callback(self) File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\core\KiwoomOpenApiPlusError.py", line 307, in callback result = future.result() File "D:\miniconda3\envs\koapy\lib\concurrent\futures_base.py", line 437, in result return self.get_result() File "D:\miniconda3\envs\koapy\lib\concurrent\futures_base.py", line 389, in get_result raise self._exception File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\utils\pyside2\QRateLimitedExecutor.py", line 44, in run result = self._fn(*self._args, self._kwargs) File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\utils\pyside2\QRateLimitedExecutor.py", line 95, in _queuedCallFn self._checkAndSleepIfNecessary(*args, *kwargs) File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\utils\pyside2\QRateLimitedExecutor.py", line 76, in _checkAndSleepIfNecessary sleep_seconds = self._limiter.check_sleep_seconds(args, kwargs) File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\core\KiwoomOpenApiPlusRateLimiter.py", line 71, in check_sleep_seconds if fn.name == "SendCondition": AttributeError: 'str' object has no attribute 'name' ERROR:grpc._server:Exception iterating responses: 'str' object has no attribute 'name' Traceback (most recent call last): File "D:\miniconda3\envs\koapy\lib\site-packages\grpc_server.py", line 461, in _take_response_from_response_iterator return next(response_iterator), True File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\grpc\KiwoomOpenApiPlusServiceServicer.py", line 255, in ConditionCall for response in handler: File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\utils\queue\QueueIterator.py", line 50, in next return self.next() File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\utils\queue\QueueBasedIterableObserver.py", line 21, in next raise error File "D:\miniconda3\envs\koapy\lib\concurrent\futures_base.py", line 328, in _invoke_callbacks callback(self) File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\core\KiwoomOpenApiPlusError.py", line 307, in callback result = future.result() File "D:\miniconda3\envs\koapy\lib\concurrent\futures_base.py", line 437, in result return self.get_result() File "D:\miniconda3\envs\koapy\lib\concurrent\futures_base.py", line 389, in get_result raise self._exception File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\utils\pyside2\QRateLimitedExecutor.py", line 44, in run result = self._fn(*self._args, self._kwargs) File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\utils\pyside2\QRateLimitedExecutor.py", line 95, in _queuedCallFn self._checkAndSleepIfNecessary(*args, *kwargs) File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\utils\pyside2\QRateLimitedExecutor.py", line 76, in _checkAndSleepIfNecessary sleep_seconds = self._limiter.check_sleep_seconds(args, kwargs) File "D:\0.work\program\secu\koapy\koapy\backend\kiwoom_open_api_plus\core\KiwoomOpenApiPlusRateLimiter.py", line 71, in check_sleep_seconds if fn.name == "SendCondition": AttributeError: 'str' object has no attribute 'name'

Tuner32 commented 1 year ago

좀 버그가 있습니다, 다음과 같이 수정하시면 잘 작동 됩니다. KiwoomOpenApiPlusRateLimiter.py 파일을 다음과 같이 수정하면 됩니다

check_sleep_seconds함수

  def check_sleep_seconds(self, fn, *args, **kwargs):
        condition_name = None
        condition_index = None
        if callable(fn):
            if fn.__name__ == "SendCondition":
                condition_name = kwargs.get("condition_name", args[1])
                condition_index = kwargs.get("condition_index", args[2])
        else:
            if fn == "SendCondition":
                condition_name = kwargs.get("condition_name", args[1])
                condition_index = kwargs.get("condition_index", args[2])
        with self._lock:
            sleep_seconds = self._comm_rate_limiter.check_sleep_seconds()
            if condition_name is not None and condition_index is not None:
                limiter_per_condition = self.get_limiter_per_condition(
                    condition_name, condition_index
                )
                sleep_seconds = max(
                    sleep_seconds, limiter_per_condition.check_sleep_seconds()
                )
            return sleep_seconds if callable(fn) else 0

add_call_history함수

def add_call_history(self, fn, *args, **kwargs):
    if callable(fn):
        if fn.__name__ == "SendCondition":
            condition_name = kwargs.get("condition_name", args[1])
            condition_index = kwargs.get("condition_index", args[2])
            with self._lock:
                limiter_per_condition = self.get_limiter_per_condition(
                    condition_name, condition_index
                )
                limiter_per_condition.add_call_history()
        else:
            with self._lock:
                self._comm_rate_limiter.add_call_history()
    else:
        with self._lock:
            self._comm_rate_limiter.add_call_history()

    def sleep_if_necessary(self, fn, *args, **kwargs):
        with self._lock:
            sleep_seconds = self.check_sleep_seconds(fn, *args, **kwargs)
            if sleep_seconds > 0:
                time.sleep(sleep_seconds)
magicwow37 commented 1 year ago

좀 버그가 있습니다, 다음과 같이 수정하시면 잘 작동 됩니다. KiwoomOpenApiPlusRateLimiter.py 파일을 다음과 같이 수정하면 됩니다

check_sleep_seconds함수

  def check_sleep_seconds(self, fn, *args, **kwargs):
        condition_name = None
        condition_index = None
        if callable(fn):
            if fn.__name__ == "SendCondition":
                condition_name = kwargs.get("condition_name", args[1])
                condition_index = kwargs.get("condition_index", args[2])
        else:
            if fn == "SendCondition":
                condition_name = kwargs.get("condition_name", args[1])
                condition_index = kwargs.get("condition_index", args[2])
        with self._lock:
            sleep_seconds = self._comm_rate_limiter.check_sleep_seconds()
            if condition_name is not None and condition_index is not None:
                limiter_per_condition = self.get_limiter_per_condition(
                    condition_name, condition_index
                )
                sleep_seconds = max(
                    sleep_seconds, limiter_per_condition.check_sleep_seconds()
                )
            return sleep_seconds if callable(fn) else 0

add_call_history함수

def add_call_history(self, fn, *args, **kwargs):
    if callable(fn):
        if fn.__name__ == "SendCondition":
            condition_name = kwargs.get("condition_name", args[1])
            condition_index = kwargs.get("condition_index", args[2])
            with self._lock:
                limiter_per_condition = self.get_limiter_per_condition(
                    condition_name, condition_index
                )
                limiter_per_condition.add_call_history()
        else:
            with self._lock:
                self._comm_rate_limiter.add_call_history()
    else:
        with self._lock:
            self._comm_rate_limiter.add_call_history()

    def sleep_if_necessary(self, fn, *args, **kwargs):
        with self._lock:
            sleep_seconds = self.check_sleep_seconds(fn, *args, **kwargs)
            if sleep_seconds > 0:
                time.sleep(sleep_seconds)

아래와 같이 동일한 오류가 나옵니다. 한 번 더 확인 가능하실까요?

codes = entrypoint.GetCodeListByCondition(condition_name,8) File "C:\Python39-32\lib\site-packages\koapy\backend\kiwoom_open_api_plus\grpc\KiwoomOpenApiPlusServiceClientStubWrapper.py", line 1298, in GetCodeListByCondition for response in self.ConditionCall( File "C:\Python39-32\lib\site-packages\grpc_channel.py", line 426, in next return self._next() File "C:\Python39-32\lib\site-packages\grpc_channel.py", line 826, in _next raise self grpc._channel._MultiThreadedRendezvous: <_MultiThreadedRendezvous of RPC that terminated with: status = StatusCode.UNKNOWN details = "Exception iterating responses: 'str' object has no attribute 'name'" debug_error_string = "UNKNOWN:Error received from peer ipv6:%5B::1%5D:5943 {created_time:"2023-04-02T22:53:58.4223803+00:00", grpc_status:2, grpc_message:"Exception iterating responses: \'str\' object has no attribute \'name\'"}"

magicwow37 commented 1 year ago

좀 버그가 있습니다, 다음과 같이 수정하시면 잘 작동 됩니다. KiwoomOpenApiPlusRateLimiter.py 파일을 다음과 같이 수정하면 됩니다

check_sleep_seconds함수

  def check_sleep_seconds(self, fn, *args, **kwargs):
        condition_name = None
        condition_index = None
        if callable(fn):
            if fn.__name__ == "SendCondition":
                condition_name = kwargs.get("condition_name", args[1])
                condition_index = kwargs.get("condition_index", args[2])
        else:
            if fn == "SendCondition":
                condition_name = kwargs.get("condition_name", args[1])
                condition_index = kwargs.get("condition_index", args[2])
        with self._lock:
            sleep_seconds = self._comm_rate_limiter.check_sleep_seconds()
            if condition_name is not None and condition_index is not None:
                limiter_per_condition = self.get_limiter_per_condition(
                    condition_name, condition_index
                )
                sleep_seconds = max(
                    sleep_seconds, limiter_per_condition.check_sleep_seconds()
                )
            return sleep_seconds if callable(fn) else 0

add_call_history함수

def add_call_history(self, fn, *args, **kwargs):
    if callable(fn):
        if fn.__name__ == "SendCondition":
            condition_name = kwargs.get("condition_name", args[1])
            condition_index = kwargs.get("condition_index", args[2])
            with self._lock:
                limiter_per_condition = self.get_limiter_per_condition(
                    condition_name, condition_index
                )
                limiter_per_condition.add_call_history()
        else:
            with self._lock:
                self._comm_rate_limiter.add_call_history()
    else:
        with self._lock:
            self._comm_rate_limiter.add_call_history()

    def sleep_if_necessary(self, fn, *args, **kwargs):
        with self._lock:
            sleep_seconds = self.check_sleep_seconds(fn, *args, **kwargs)
            if sleep_seconds > 0:
                time.sleep(sleep_seconds)

정상 작동합니다. 캐쉬에 남아있어서 그런걸까요? 하여튼 잘 작동합니다.