PYTHON/AUTO TRADE SYSTEM

[자동 매매 시스템 구축하기] 상하한가 데이터 계산하기 (4) - 실제 데이터 확인해보기

  • -

지난 게시글에서 종목코드와 전일 종가 데이터를 인자로 전달받아 상한가와 하한가를 계산해내는 함수를 구현했다. 그렇다면 이제 실제 데이터를 입력해봄으로써 상하한가 데이터를 올바르게 계산하고 있는지를 확인해보도록 하자. 여태까지 제작했던 게시글을 새로운 파일에서 새로운 클래스 내에 입력해서 테스트해볼 것인데, 그 코드는 아래와 같다.

  def __init__(self):  부분에서는 해당 클래스가 실행되었을 때 실행되는 부분을 의미한다. 즉, test라는 클래스가 호출됐을 때   self.kospi_code_list  와   self.kosdaq_code_list  변수 내에 각각 ['005930', '066580'], ['000020', '000040']이라는 데이터를 입력하도록 하는 것이다. 다만 우리는 실제 동작을 확인할 때에는 이와 같은 작업이 불필요하다. 왜냐하면 호가가격단위 절사 함수를 생성하는 게시글에서 우리의 Open API가 실행됐을 때   self.kospi_code_list  와   self.kosdaq_code_list  변수를 생성하도록 했기 때문이다. 

class test:

    def __init__(self):
        self.kospi_code_list = ['005930', '066580']
        self.kosdaq_code_list = ['000020', '000040']


    def cal_limit(self, item_code, yclose):
        yclose = int(yclose)
        price_range = float(yclose * 0.3)

        ## 코스피 종목의 상하한가 계산..
        if item_code in self.kospi_code_list:
            price_range = self.kospi_price_range(price_range)
            var1 = float(yclose + price_range)
            up_price = self.kospi_price_range(var1)
            var2 = float(yclose - price_range)
            down_price = self.kospi_price_range(var2)
            return up_price, down_price

        ## 코스닥 종목의 상하한가 계산
        elif item_code in self.kosdaq_code_list:
            price_range = self.kosdaq_price_range(price_range)
            var1 = float(yclose + price_range)
            up_price = self.kosdaq_price_range(var1)
            var2 = float(yclose - price_range)
            down_price = self.kosdaq_price_list(var2)
            return up_price, down_price

        else:
            set_logging.log.error(f"※※※ [{item_code}]가 코스피 및 코스닥 종목 정보에 없습니다.")



    def kospi_price_range(self, price):
        price = float(price)

        if 0 <= price < 1000:  ## 1,000원 미만 : 1원 단위로 정수 만들기
            return self.cal_price_range(price, 1)
        elif 1000 <= price < 5000:  ## 1,000원~5,000원 : 5원 단위
            return self.cal_price_range(price, 5)
        elif 5000 <= price < 10000:  ## 5,000원~10,000원 : 10원 단위
            return self.cal_price_range(price, 10)
        elif 10000 <= price < 50000:  ## 10,000원~50,000원 : 50원 단위
            return self.cal_price_range(price, 50)
        elif 50000 <= price < 100000:  ## 50,000원~ 100,000원 : 100원 단위
            return self.cal_price_range(price, 100)
        elif 100000 <= price < 500000:  ## 100,000원~500,000원 : 500원 단위
            return self.cal_price_range(price, 500)
        elif 500000 <= price:  ## 500,000원 이상: 1000원 단위
            return self.cal_price_range(price, 1000)



    def kosdaq_price_range(self, price):
        price = float(price)

        if 0 <= price < 1000:  ## 1,000원 미만 : 1원 단위
            return self.cal_price_range(price, 1)
        elif 1000 <= price < 5000:  ## 1,000원~5,000원 : 5원 단위
            return self.cal_price_range(price, 5)
        elif 5000 <= price < 10000:  ## 5,000원~10,000원 : 10원 단위
            return self.cal_price_range(price, 10)
        elif 10000 <= price < 50000:  ## 10,000원~50,000원 : 50원 단위
            return self.cal_price_range(price, 50)
        elif 50000 <= price:  ## 50,000원 이상 : 100원 단위
            return self.cal_price_range(price, 100)



    def cal_price_range(self, price, measure):

        for i in range(0, 1001):        
            if price % measure == 0:
                return price
            elif price % measure != 0:
                price = price - 1

 

 

코스피 종목부터 확인해보자.

실제 시장에서 동작 여부를 확인하는 것이 아니기 때문에, 올바른 인자가 전달되었을 때 올바른 상한가와 하한가를 전달받고 있는지를 확인해보면 된다. 다만 종목코드의 경우에는 실제 종목코드와 무관하게 '005930' 또는 '066580'이라는 코드로만 전달해주면 된다. 즉, 실제로 코스피 시장에 상장되어 있는 셀트리온[068270] 종목을 조회하고 싶다 하더라도 종목코드 인자로는 '005930' 또는 '066580'을 전달해주면 된다. 왜냐하면 그 종목이 진짜로 코스피 시장에 상장되어 있는지를 판단하는 것은 Open API 내에서 생성된   self.kospi_code_list  또는   self.kosdaq_code_list  에 입력되어 있기 때문이다. 지금은 임시로 생성해둔 것이니 그냥 코스피 종목이라면 '005930'을, 코스닥 종목이라면 '000020'을 입력한 후 전일 종가 데이터를 전달해주도록 하자.

위의 클래스와 데이터를 입력하는 방법은 아래와 같다.

test().cal_limit(종목코드, 전일종가)

 

위의 두 종목은 각각 2022년 12월 2일, 셀트리온과 삼성전자의 장마감 이후 호가 화면이다. 셀트리온의 전일종가는 178,500원, 삼성전자의 전일종가는 62,600원으로 기록되어 있다. 그렇다면 이 두 종목의 상하한가 데이터를 얻기 위해서는 어떻게 하면 될까? 앞서 말했듯 종목 코드는 단순히 시장 구분 용도로만 전달하고 그 뒤에는 전일 종가 데이터를 전달해주면 된다.

## 셀트리온
test().cal_limit('005930', 178500)

## 삼성전자
test().cal_limit('005930', 62600)

## 셀트리온
>>> print(test().cal_limit('005930', 178500))
>>> (232000.0, 125000.0)

## 삼성전자
>>> print(test().cal_limit('005930', 62600))
>>> (81300.0, 43850.0)

 

그 데이터를 비교해본 결과는 다음과 같다. 삼성전자의 경우 코스피에 상장된 종목으로서 호가가격단위가 50원인데, 실제 시장에서의 하한가는 43,900원임을 확인할 수 있다. 다시 말해, 실제 시장에서 계산되는 상하한가 데이터는 우리가 갖고 있는 이론과 맞지 않다거나, 추가적으로 적용되는 데이터가 있다는 것을 의미한다.

 

 


728x90

 

 

그게 뭘까?

근본적으로 코드 제작 과정에서 높인 부분은 바로 호가가격단위를 판단하기 위한 기준이 되는 값은 계산 후의 상한가 및 하한가 값이 아니라 상하한가 계산의 기준이 되는 전일종가 기준이라는 것이다. 다시 말해, 삼성전자의 경우 전일종가가 62,600원(코스피 종목, 5만원 이상은 100원)이었기 때문에 가격제한폭 값과 상하한가 모두 100원의 호가가격단위를 갖게 되는 것이다. 그렇다면 우리는 어떻게 수정해야 할까? 방법은 간단하다. 전일종가를 기준으로 호가가격단위를 얻어온 후에, 그 값을 기준으로 데이터들을 계산하면 된다.

이전 게시글에서 제작했던 함수들을 보며   self.kospi_price_range  와   self.kosdaq_price_range  에서 가격대별로 호가가격단위를 적용하여   self.cal_price_range  함수의 인자로 전달하도록 하고 있는데, 그 부분을   self.cal_price_range  함수를 실행하도록 하는 것이 아닌 호가가격단위를 반환받도록 수정해보자.
※ Line 1~30: 기존코드, Line 33~62: 수정코드

## 기존 코드
def kospi_price_range(self, price):
    price = float(price)
    if 0 <= price < 1000:  ## 1,000원 미만 : 1원 단위로 정수 만들기
        return self.cal_price_range(price, 1)
    elif 1000 <= price < 5000:  ## 1,000원~5,000원 : 5원 단위
        return self.cal_price_range(price, 5)
    elif 5000 <= price < 10000:  ## 5,000원~10,000원 : 10원 단위
        return self.cal_price_range(price, 10)
    elif 10000 <= price < 50000:  ## 10,000원~50,000원 : 50원 단위
        return self.cal_price_range(price, 50)
    elif 50000 <= price < 100000:  ## 50,000원~ 100,000원 : 100원 단위
        return self.cal_price_range(price, 100)
    elif 100000 <= price < 500000:  ## 100,000원~500,000원 : 500원 단위
        return self.cal_price_range(price, 500)
    elif 500000 <= price:  ## 500,000원 이상: 1000원 단위
        return self.cal_price_range(price, 1000)

def kosdaq_price_range(self, price):
    price = float(price)
    if 0 <= price < 1000:  ## 1,000원 미만 : 1원 단위
        return self.cal_price_range(price, 1)
    elif 1000 <= price < 5000:  ## 1,000원~5,000원 : 5원 단위
        return self.cal_price_range(price, 5)
    elif 5000 <= price < 10000:  ## 5,000원~10,000원 : 10원 단위
        return self.cal_price_range(price, 10)
    elif 10000 <= price < 50000:  ## 10,000원~50,000원 : 50원 단위
        return self.cal_price_range(price, 50)
    elif 50000 <= price:  ## 50,000원 이상 : 100원 단위
        return self.cal_price_range(price, 100)


## 수정 코드
def kospi_price_range(self, price):
    price = float(price)
    if 0 <= price < 1000:  ## 1,000원 미만 : 1원 단위로 정수 만들기
        return 1
    elif 1000 <= price < 5000:  ## 1,000원~5,000원 : 5원 단위
        return 5
    elif 5000 <= price < 10000:  ## 5,000원~10,000원 : 10원 단위
        return 10
    elif 10000 <= price < 50000:  ## 10,000원~50,000원 : 50원 단위
        return 50
    elif 50000 <= price < 100000:  ## 50,000원~ 100,000원 : 100원 단위
        return 100
    elif 100000 <= price < 500000:  ## 100,000원~500,000원 : 500원 단위
        return 500
    elif 500000 <= price:  ## 500,000원 이상: 1000원 단위
        return 1000

def kosdaq_price_range(self, price):
    price = float(price)
    if 0 <= price < 1000:  ## 1,000원 미만 : 1원 단위
        return 1
    elif 1000 <= price < 5000:  ## 1,000원~5,000원 : 5원 단위
        return 5
    elif 5000 <= price < 10000:  ## 5,000원~10,000원 : 10원 단위
        return 10
    elif 10000 <= price < 50000:  ## 10,000원~50,000원 : 50원 단위
        return 50
    elif 50000 <= price:  ## 50,000원 이상 : 100원 단위
        return 100

 

 

이제   def cal_limit  함수 내에서는 시장을 구분하고 있는데, 이 시장 구분 조건문 아래 부분을 아래의 코드와 같이 수정해주면 된다.   

def cal_limit(self, item_code, yclose):
    yclose = int(yclose)
    price_range = float(yclose * 0.3)

    ## 코스피 종목의 상하한가 계산..
    if item_code in self.kospi_code_list:
        measure = self.kospi_price_range(yclose)                                    ## 호가가격단위 반환
        price_range = self.cal_price_range(yclose*0.3, measure)                     ## 가격제한폭 계산
        up_price = self.cal_price_range(float(yclose + price_range), measure)       ## 상한가 계신
        down_price = self.cal_price_range(float(yclose - price_range), measure)     ## 하한가 계산
        return up_price, down_price

    ## 코스닥 종목의 상하한가 계산
    elif item_code in self.kosdaq_code_list:
        measure = self.kosdaq_price_range(yclose)                                   ## 호가가격단위 반환
        price_range = self.cal_price_range(yclose*0.3, measure)                     ## 가격제한폭 계산
        up_price = self.cal_price_range(float(yclose + price_range), measure)       ## 상한가 계산
        down_price = self.cal_price_range(float(yclose - price_range), measure)     ## 하한가 계산
        return up_price, down_price

    else:
        set_logging.log.error(f"※※※ [{item_code}]가 코스피 및 코스닥 종목 정보에 없습니다.")

 

지금까지 작성한 함수들의 기능을 정리하면 다음과 같다.

  • self.kospi_price_range(self, price) : 전달받은 전일종가를 기준으로 코스피의 호가가격단위를 반환
  • self.kosdaq_price_range(self, price) : 전달받은 전일종가를 기준으로 코스닥의 호가가격단위를 반환
  • self.cal_price_range(self, price, measure) : 전달받은 을 전달받은 호가가격단위를 기반으로 계산

 

이제 새롭게 작성한 코드를 기반으로 앞서 계산에 실패했던 여러 종목들의 상하한가 데이터를 새롭게 계산해보도록 하자. 이제 정상동작하는 것을 확인할 수 있다.

왼쪽부터 차례대로 삼성전자, 셀트리온, 비에이치

## 삼성전자(코스피)
>>> print(test().cal_limit('005930', 62600))
>>> (81300.0, 43900.0)

## 셀트리온(코스피)
>>> print(test().cal_limit('005930', 178500))
>>> (232000.0, 125000.0)

## 비에이치(코스닥)
>>> print(test().cal_limit('000020', 26700))
>>> (34700.0, 18700.0)

 

 

하지만 아래의 데이터를 입력하면 다음(Line 3~7)과 같은 오류가 발생한다.

>>> test().cal_limit('000020', 1795)

  File "C:/Users/a/PycharmProjects/trading/justtest.py", line 92, in <module>
    test().cal_limit('000020', 1795)
  File "C:/Users/a/PycharmProjects/trading/justtest.py", line 44, in cal_limit
    up_price = self.cal_price_range(float(yclose + price_range), measure)       ## 상한가 계산
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'

 

이 오류가 발생하는 이유는 단순하다. 앞서 살펴봤던 세 가지 예시의 경우에는 아무런 오류가 발생하지 않았음에도 1,765원을 입력하니 TypeError가 발생했는데, 실제로 1,765원에 0.3을 곱해보면 529.5원이 나오기 때문에 발생하는 오류이다. 다시 말해, 529.5원과 같이 소수점이 존재하는 가격대에서는 데이터 계산이 불가하다는 것이다.

그렇다면 소수점이 존재하는 부분에 대해서는 어떻게 해결해야 할까? 이는 호가가격단위 미만 절사의 개념에 대해 살펴보면 된다. 전일종가는 1,765원이고 가격제한폭이 529.5원이라는 값을 가질 때 하한가는 얼마가 되어야 할까? 

위의 호가 화면을 보면 전일 종가가 1,765원인데 하한가는 1,240원으로 표시되어 있다. 이는 기존의 가격제한폭인 529.5원을 호가가격단위 미만 절사를 통해 가격제한폭을 525원으로 만들어주면 된다. 

  • 상한가 2,290원 = 1,765 + 525
  • 하한가 1,240원 = 1,765 - 525

그렇다면 결국 529.5원와 같이 소수점이 존재하는 경우라 하더라도 소수점을 없애고 계산하더라도 아무런 문제가 발생하지 않는다. 따라서 TypeError가 발생하지 않도록 int() 문을 통해 소수점을 아예 삭제해주도록 하자.
※ Line 11

## 기존 코드
def cal_price_range(self, price, measure):
    for i in range(0, 1001):        
        if price % measure == 0:
            return price
        elif price % measure != 0:
            price = price - 1

## 수정 후 코드
def cal_price_range(self, price, measure):
    price = int(price)
    
    for i in range(0, 1001):        
        if price % measure == 0:
            return price
        elif price % measure != 0:
            price = price - 1

 

실행 결과는 다음과 같다.

>>> print(test().cal_limit('000020', 1765))
>>> (2290, 1240)

 

 

현재까지 작성한 상하한가 계산 클래스 코드 전문은 아래의 더보기를 클릭하면 확인할 수 있습니다.

더보기
class test:

    def __init__(self):
        self.kospi_code_list = ['005930', '066580']
        self.kosdaq_code_list = ['000020', '000040']


    def cal_limit(self, item_code, yclose):
        yclose = int(yclose)
        price_range = float(yclose * 0.3)

        if item_code in self.kospi_code_list:
            measure = self.kospi_price_range(yclose)                                    ## 호가가격단위 반환
            price_range = self.cal_price_range(yclose*0.3, measure)                     ## 가격제한폭 계산
            up_price = self.cal_price_range(float(yclose + price_range), measure)       ## 상한가 계신
            down_price = self.cal_price_range(float(yclose - price_range), measure)     ## 하한가 계산
            return up_price, down_price

        elif item_code in self.kosdaq_code_list:
            measure = self.kosdaq_price_range(yclose)                                   ## 호가가격단위 반환
            price_range = self.cal_price_range(yclose*0.3, measure)                     ## 가격제한폭 계산
            up_price = self.cal_price_range(float(yclose + price_range), measure)       ## 상한가 계산
            down_price = self.cal_price_range(float(yclose - price_range), measure)     ## 하한가 계산
            return up_price, down_price

        else:
            set_logging.log.error(f"※※※ [{item_code}]가 코스피 및 코스닥 종목 정보에 없습니다.")



    def kospi_price_range(self, price):
        price = float(price)
        if 0 <= price < 1000:  ## 1,000원 미만 : 1원 단위로 정수 만들기
            return 1
        elif 1000 <= price < 5000:  ## 1,000원~5,000원 : 5원 단위
            return 5
        elif 5000 <= price < 10000:  ## 5,000원~10,000원 : 10원 단위
            return 10
        elif 10000 <= price < 50000:  ## 10,000원~50,000원 : 50원 단위
            return 50
        elif 50000 <= price < 100000:  ## 50,000원~ 100,000원 : 100원 단위
            return 100
        elif 100000 <= price < 500000:  ## 100,000원~500,000원 : 500원 단위
            return  500
        elif 500000 <= price:  ## 500,000원 이상: 1000원 단위
            return 1000


    def kosdaq_price_range(self, price):
        price = float(price)
        if 0 <= price < 1000:  ## 1,000원 미만 : 1원 단위
            return 1
        elif 1000 <= price < 5000:  ## 1,000원~5,000원 : 5원 단위
            return 5
        elif 5000 <= price < 10000:  ## 5,000원~10,000원 : 10원 단위
            return 10
        elif 10000 <= price < 50000:  ## 10,000원~50,000원 : 50원 단위
            return 50
        elif 50000 <= price:  ## 50,000원 이상 : 100원 단위
            return 100


    def cal_price_range(self, price, measure):
        price = int(price)
        
        for i in range(0, 1001):
            if price % measure == 0:
                return price
            elif price % measure != 0:
                price = price - 1

 

 


728x90
반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.