본문 바로가기
무모한 도전-주식 인공지능 만들기/지식 정리 창고

손과 발 역할을 하는 프로그램의 정리 part1

by 인터넷떠돌이 2020. 4. 2.
반응형

안녕하세요?

 

드디어 요청이 많이 들어오고 많은 분들이 따라 하시다가 포기하는 일이 속출하는 부분인 제 프로그램의 중요한 부분에 대해서 한번 정리하는 시간이 왔다고 할 수 있습니다. 일단 여기서 나오는 프로그램은 가장 최근까지 직접 키움증권 서버에 접속을 해서 단기적으로는 이익을 보고, 장기적으로는 손해를 보는 모의투자를 실행한 프로그램을 정리 하고자 합니다.

 

들어가기 전에 일단 전체 코드를 올리기만 할까도 생각해 보았습니다만, 그렇게 하면 저야 편하겠지만, 보기만 해도 눈이 어지러운 코드만 봐서는 큰 발전이 없을 것이라는 생각이 들었습니다. 그래서 일단 제가 프로그램을 어쩌다가 여기까지 만들게 되었는지 코드의 구조랑 거기에 사용된 기본적인 지식에 대해서 먼저 정리를 하고서, 마지막에 코드를 찍어서 올리는 것이 가장 나은 방법이 되리라 생각을 했습니다.

 

먼저 언급을 해야 하는 것으로는 위 스크린샷처럼 제가 어떤 식으로 보통 코드를 작성하는 지에 대한 대략적인 구조입니다. 가장 맨 윗줄에 외부에서 가지고 오는 라이브러리를 import해오도록 배치하고, 그 다음에 클래스를 배치하도록 합니다. 이렇게만 해서는 실행이 되지 않기 때문에 실행을 위해서 if name으로 시작하는 뭐라고 해야 할까요? 최종적으로 실행한다고 하는 신호를 주도록 합니다. 물론 이건 어디까지나 기본적인 구조이지, 상황에 따라서 그때 그때 달라집니다.

 

일단 먼저 언급을 해야 하는 것으로는 먼저, 거래를 직접 키움증권 서버로 보내는 프로그램을 작동시키기 이전에 부모 프로세스라고 할 수 있는 코드를 작성해서 작동 시켜야 합니다. 왜 이런 부모 프로세스를 만들었느냐 하면, 바로 키움증권에서 제공하는 Open API에서는 로그아웃 기능을 이제 더 이상 제공하지 않기 때문에, 하는 수 없이 프로그램을 종료 시켜야 하기 때문입니다.

 

그래서 일단 부모 프로세스는 어떻게 작동이 되느냐 하면, 먼저 클래스의 밖에서 존해를 하는 main()과 main2()라는 메서드가 있습니다. 이 메서드들을 이용해서 처음에 시작을 하면, main메서드가 작동을 하면서 클래스를 인스턴스화 - 활성화 시키고, 작업이 끝나면, main2를 부릅니다. 그럼 main2가 불러지면 다시 main을 부르는 형식으로 되어 있습니다.

 

왜 이런 식으로 부모 프로세스를 만들었느냐 하면, 다시한번 말하지만, 키움증권 Open API에서는 과거에 로그아웃 기능을 제공했다가 이제는 조회횟수 제한을 두기 위해서 더 이상은 로그아웃 기능을 제공하지 않습니다. 하는 수 없이 프로그램을 종료했다가 다시 실행해야 하는데, 이 작업을 일일히 몇분 단위로 수작업으로 해도 되지만, 저는 자동으로 하기 위해서 이렇게 부모 프로세스를 만들어 주도록 했습니다.

 

여기서 눈썰미 좋으신 분들은 아시겠지만, main과 main2메서드는 서로를 호출하는 관계인데 이게 너무 자주되면 파이썬에서는 자동으로 종료를 시켜 버립니다. 이를 방지하기 위해서는 sys.setrecursionlimit()라는 것을 상당히 많이 잡아 주어야 합니다. 기본적으로 1000정도에서 종료가 되기 때문에, 이를 많이 주었습니다.

 

 

그래서 실질적으로 작업을 하는 자식 프로세스인 child process로 들어오게 되면, 여기서 부터는 QThread라고 해서 PyQt5에서 사용할 수 있는 멀티 스레드를 사용하는 것을 볼 수 있습니다. 왜 이렇게 QThread를 이용해서 각각의 스레드를 만들었느냐 하면, 한개의 종목만 가지고 매도/매수를 결정시키지 않고 여러개의 종목을 동시에 작업하기 위해서 이렇게 만들어 준 것입니다.

 

다음으로는 자식 프로세스가 전체적으로 어떤 구조로 되어 있는지에 대해서 한번 이야기를 할까 합니다. 앞서 말했다 시피 저는 1개의 종목만이 아니라 여러개의 종목 - 거의 20개 넘는 종목을 계산해 보기 위해서, QThread를 사용했는데 이를 위해서는 일단 2개의 클래스를 만들어야 했습니다. 메인이 되는 클래스가 class MainWindow인데, 여기서는 시작하자 마자 키움증권 서버에 로그인을 먼저 합니다.

 

그리고나서 QThread를 세팅하도록 하고, 그 다음에는 GUI코드라고 해서 Qt designer를 거치지 않고, 그냥 코드만으로 만들 수도 있습니다. 이렇게 코드를 입력하고 나서 다음으로는 MainWindow안에 있는 메서드를 호출해서 for 문을 이용한 QThread를 여러개 생성해 주도록 하니다.

 

이렇게 해서 들어가면 QThread를 실행시키면, 이 역할을 하는 클래스가 바로 Worker입니다. 여기서 실질적으로 매도/매수를 두뇌 역할을 하는 프로그램에 물어서 결과를 받고, 키움증권에 주문을 전송하는 역할을 하게 되는데, 일단 여기서는 먼저 txt파일로 정보를 물어오도록 하고, 그전에는 kiwoom.py를 사용가능하도록 만들어 주는 작업을 하게 됩니다.

 

여기서 instance라고 해서 이게 무슨 사용이 가능하니 뭐니 하는데, 이게 무엇이냐고 하면, 먼저 여기에 대해서 설명을 하고 넘어가야 합니다. 일단 위 그림에서 볼 수 있는 것처럼 self.변수 = kiwoom()이라는 코드를 작성하게 되는데, 이걸 해 주어야 kiwoom.py에 있는 메서드와 변수를 사용가능합니다.

 

이런 과정을 instance라고 부르는 것으로 보이는데, 일단 제 능력으로는 자세한 용어는 잘 모르겠습니다. 다만 이렇게 컴퓨터에 같은 kiwoom.py파일을 사용가능 하도록 만들어 주는 작업을 여러번 할 수 있는데, 그 결과 컴퓨터의 메모리 상에서는 여러개의 kiwoom.py의 인스턴스가 생성이 됩니다. 그런데 문제는 이 인스턴스들이 모두 각각 다른 객체라고 해야 할까요? 아무튼 같은 것이 아니라는 데 문제가 있습니다.

 

그럼 여기서 나오는 싱글턴 혹은 싱글톤 singleton이라는 것이 무엇이냐고 하면, 일단 같은 kiwoom.py라는 파이썬 스크립트를 사용가능하게 하기 위해서 변수 = kiwoom()이라는 것을 여러번 해서, 여러개의 kiwoom.py의 인스턴스를 만들어도, 이게 전부 같은 객체라고 해야 할까요? 일단 같은 것이라고 할 수 있습니다.

 

그럼 여기서 여러분이 드실 의문이, 왜 이렇게 싱글톤이니 하는 것이 나오면서, 왜 이런 작업을 하게 되는 것이냐고 말하실 것인데, 이게 바로 아래에서 나오는 것과 같은 이유가 있어서 이렇게 복잡한 것을 사용하게 되었습니다.

 

일단 싱글톤 혹은 싱글턴을 사용하지 않았을 경우에는 위 그림에서 볼 수 있는 것과 같이 다른 인스턴스에서 로그인을 해도, 정작 그건 1번 kiwoom.py 인스턴스에서 로그인을 한 것이지, 2번 kiwoom.py의 인스턴스가 로그인을 한 것이 아닙니다. 그래서 키움증권 서버와 통신이 될 리가 없습니다.

 

이 문제를 해결하게 해 주는 것이 바로 싱글톤이었고, 이걸 찾아내서 적용하는데 상당한 시간이 걸리기는 했습니다. 여기서 한가지 문제가 나오는데, 왜 여러개의 kiwoom.py의 인스턴스를 생성하느냐 하면, 바로 아래와 같은 이유가 생겨서 나온 것 입니다.

 

일단 로그인을 QThread를 만들기 전에 있는 클래스에서 했습니다. 그런데 이렇게 해서 나중에 QThread를 작동시키면, 여기서 kiwoom.py에 있는 메서드랑 이런 것을 작동 시켜야 하는데, 여기서 문제가 생기는 것 입니다. 싱글턴을 사용하지 않았을 경우에는 같은 kiwoom.py가 아니기 때문에, 정말로 키움증권 서버에 접속을 해야 하는 스레드에서는 아무것도 할 수 없는 현상이 벌어지는 것 입니다.

 

마지막으로 그럼 각각의 스레드에서 로그인을 하면 되지 않느냐 하면, 여기서 한가지 문제가 생기게 됩니다. 일단 키움증권 서버에서는 같은 아이디를 가지고서 여러번 멀티플로 로그인을 하는 것을 막았습니다. 그래서 각각의 스레드에서 각각의 로그인을 한다 = 멀티 로그인을 한다가 되어서 키움증권 서버가 접속을 차단해 버립니다.

 

결국 이런 문제를 해결하면서도 여러개의 종목을 한꺼번에 보기 위해서 일단 QThread를 적용해야 했습니다. 그리고 이를 위해서 싱글턴을 따라서 적용해야 했습니다. 다만 여기서 나와 있는 내용이 전부가 아니라 이제 겨우 시작이라고 할 수 있습니다. 일단 제가 프로그램을 짜는 방식이 절대적이지 도 않고, 단점도 많이 있습니다. 그리고 이번 정리는 겨우 빙산의 일각이라는 소리가 나올 정도로 제가 짜 놓고서도, 모든 걸 알고서 한꺼번에 짠 것이 아니라, 문제에 부딪치면 시행착오를 거쳐서 문제를 해결하면서 만들었습니다.

 

일단 스파게티 코드라고 하면 할 수 있지만, 그래도 최대한 피하려고 신경을 쓰기는 썻습니다. 일단 여기 지식정리 창고에 들어가는 포스팅은 기존의 실험노트처럼 실패한 내용을 그냥 쓸 수 없고 해서, 최대한 내용을 정리해서 올리고자 합니다만, 문제는 설명을 해야 하다 보니, 저도 시간이 좀 걸리기는 걸릴 것으로 생각이 됩니다. 그래도 최대한 서둘러서 이 손과 발의 역할을 하는 프로그램을 정리하고, 마지막에는 전체 코드의 스크린샷을 올리도록 하겠습니다.

반응형