PYTHON/AUTO TRADE SYSTEM

[자동 매매 시스템 구축하기] 알고리즘 구축하기 (11) - 알고리즘 구현하기 ⑥

  • -

지난 게시글에서 특정 종목의 매수 예정 가격을 계산해두었는데, 아직 수행되지 않은 한 가지 작업이 남아 있다. 바로 매도 예정 가격이다. 사실 첫 게시글에서 5일 이동평균선이 10일 이동평균선을 이탈했을 때 매도하는 전략을 수립해서 그 전략을 기반으로 거래를 진행하도록 하는 자동 매매 시스템을 구현하고자 했으나, 사실 따지고 보면 5일 이동평균선과 10일 이동평균선 간 데드 크로스 역시 특정 봉이 발생해야 판단할 수 있는 개념이기 때문에, 사전에 매도 예정 가격을 계산하기가 어렵다. 따라서 원래는 매도 예정 가격도 함께 계산하여 매수 함수와 매도 함수를 모두 작성하려 했으나, 아쉽게도 매수 함수만 작성하고 그 방법을 응용해서 매도 함수를 직접 작성하는 방법밖에는 없을 것 같다는 생각을 하게 된다.

매도 예정 가격을 처리하는 것에 대한 설명은 제외하겠지만, 일단 이동평균선 간 데드 크로스가 발생했다는 사실을 잡아내는 것이 어려운 일은 아니다. 다만 본인은 이 전략이 아무런 효과가 없다고 생각하기 때문에 귀찮은 작업을 생략하는 것이다. 물론, 이 전략을 이용해서 막대한 수익을 내는 트레이더도 있기에 공식적으로 부인하지는 않겠지만, 적어도 본인 입장에서는 썩 좋아하지는 않는다. 이동평균선을 이용한 거래는 충분히 동의하지만, 이동평균선만을 이용한 거래는 동의하지 않기 때문이다.(그 분들 역시 이동평균선 하나만으로 거래하진 않는다.)

 

 

매수 예정 가격 데이터 전달받기

이제 algorithm_1.py 파일 내에서 이동평균선 간 골든 크로스 지점을 찾아내고 매수 예정 가격을 계산해내는 기능까지 모두 구현했으니, 이제는 해당 매수 예정 가격 데이터를 어떻게 main.py 파일로 가져와서 사용할 수 있는지를 생각해보아야 한디. 그러기 위해서는 일단 매수 예정 가격을 변수화하여 계산한 후에, 매수 예정 가격 데이터를 계산하는 algorithm_1.py 파일 내에서 데이터프레임 형태의 데이터를 전달하도록 해보자. 

말이 좀 어렵게 느껴질 수 있긴 한데, 계산된 매수 예정 가격 데이터를 여러 데이터와 함께 데이터프레임으로 만들어서 main.py 파일에 전달해준 후, main.py 파일 내에서는 종목별로 계산된 매수 예정 가격 데이터를 합쳐서 하나의 큰 데이터프레임으로 만들어서 매수 예정 종목 데이터를 관리할 것이다.

def run(self):
    self.highest_price = self.start_price
    self.lowest_price = 0
    self.pos_buy = False

    for data in self.tem_min5_data.itertuples():
        high = int(data.high)
        low = int(data.low)

        if high > self.highest_price:
            print(f"고가가 갱신되었습니다... {data.date}, {data.time}")
            self.highest_price = high  ## 고가 데이터 재입력
            self.buy_price = (self.start_price + self.highest_price) / 2
            self.lowest_price = high

            if high == self.standard_high:
                self.pos_buy = True

        if low <= self.lowest_price:
            print(f"저가가 갱신되었습니다... {data.date}, {data.time}")
            self.lowest_price = low

            if self.pos_buy:
                if low < self.buy_price:
                    print(f"매수 예정 가격에 닿았습니다... [{data.time}] {low}/{self.buy_price}")
                    pass

    print(self.buy_price)

일단 우리가 이전 게시글까지 작성한 매수 예정 가격 계산 코드 중   def run(self)  의 전체 구조이다. 현재 코드에서 수정해야 하는 부분이 있다면, 바로   Line 25  부분이다. 왜냐하면 해당 종목을 대상으로 계산한 매수 예정 가격이 있는데, 특정 시점의 저가 데이터가 매수 예정 가격보다 낮았다는 것은 곧 매수가 가능했었다는 것을 의미하며 이는 곧 지금 매수에 가담하기에는 늦었다는 의미가 되기 때문이다. 따라서   if low < self.buy_price:  가 충족된 경우에는 알고리즘에 의한 매수 예정 가격을 계산하는 절차를 중지해야 한다.

그렇다면 for문이라는 어떤 반복적인 코드 수행을 멈추도록 하는   break  를 넣어주어야 할지 아니면 해당 부분은 건너뛰도록 하는   continue  를 넣어주어야 할지 고민될 수도 있겠지만, 정답은 둘 다 아니다. 우리는   return False  를 사용함으로써 해당 종목 코드의 매수 예정 가격을 계산하는 과정을 아예 중지하고 False라는 값을 반환함으로써 계산된 데이터가 없음을 main.py 파일에게 알려주어야 한다.
※ Line : 26, 28

def run(self):
    self.highest_price = self.start_price
    self.lowest_price = 0
    self.pos_buy = False

    for data in self.tem_min5_data.itertuples():
        high = int(data.high)
        low = int(data.low)

        if high > self.highest_price:
            print(f"고가가 갱신되었습니다... {data.date}, {data.time}")
            self.highest_price = high  ## 고가 데이터 재입력
            self.buy_price = (self.start_price + self.highest_price) / 2
            self.lowest_price = high

            if high == self.standard_high:
                self.pos_buy = True

        if low <= self.lowest_price:
            print(f"저가가 갱신되었습니다... {data.date}, {data.time}")
            self.lowest_price = low

            if self.pos_buy:
                if low < self.buy_price:
                    print(f"매수 예정 가격에 닿았습니다... [{data.time}] {low}/{self.buy_price}")
                    return False

    return True, self.buy_price

26번째 줄을 보면 특정 시점의 저가 데이터가 매수 예정 가격보다 낮았다면   return False  을 통해 main.py 파일에   False  라는 값을 전달하도록 했으며, 28번째 줄을 보면 매수 예정 가격보다 낮았던 시점이 없다면   return True, self.buy_price  를 통해 main.py 파일에   True  와 함께 매수 예정 가격 데이터(  self.buy_price  ) 변수를 전달하도록 했다. 

다만 여기서 데이터를 사용하고자 할 때   return True, self.buy_price  의 경우 해당 데이터를   result  라는 변수에 저장하도록 했을 때   result[0]  은 True,   result[1]  은 self.buy_price를 가리키게 된다. 하지만   return False  의 경우 해당 데이터를   result  라는 변수에 저장하도록 한 후에   result[0]  을 사용하게 되면   IndexError  가 발생할 것이다. 왜냐하면 현재   result  변수에는   False  라는 값만 입력돼있기 때문이다. 

우리는 main.py 파일 내에서   if result[0] == True  라는 코드를 사용할 것이기 때문에, algorithm_1.py 파일로부터   False  라는 값이 전달되더라도   result[0]  을 사용했을 때   IndexError  가 발생하지 않도록 False와 함께 임의적인 값을 하나 더 반환해주어야 한다. 물론 그 값은 어떤 값이든 간에 상관이 없기 때문에 반환할 하나의 값만 있으면 된다.
※ Line : 26

def run(self):
    self.highest_price = self.start_price
    self.lowest_price = 0
    self.pos_buy = False

    for data in self.tem_min5_data.itertuples():
        high = int(data.high)
        low = int(data.low)

        if high > self.highest_price:
            print(f"고가가 갱신되었습니다... {data.date}, {data.time}")
            self.highest_price = high  ## 고가 데이터 재입력
            self.buy_price = (self.start_price + self.highest_price) / 2
            self.lowest_price = high

            if high == self.standard_high:
                self.pos_buy = True

        if low <= self.lowest_price:
            print(f"저가가 갱신되었습니다... {data.date}, {data.time}")
            self.lowest_price = low

            if self.pos_buy:
                if low < self.buy_price:
                    print(f"매수 예정 가격에 닿았습니다... [{data.time}] {low}/{self.buy_price}")
                    return False, '0'

    return True, self.buy_price

 

 


728x90

 

 

매수 예정 가격 데이터를 데이터프레임화하기

algorithm_1.py 파일 내에서 계산된 매수 예정 가격 데이터를 데이터프레임화를 시키기 위해서는 ①algorithm.py 파일 내에서 데이터프레임 형태로 데이터를 생성하여 전달해주는 방법과 ②main.py 파일 내에서 데이터프레임을 생성하여 algorithm_1.py 파일로부터 전달받은 데이터를 입력하는 방법이 있다. (물론, 본인은 전자의 방법을 사용하므로 전자를 소개하도록 하겠다.)

일단 데이터를 특정 데이터프레임에 입력하기 전에 앞서, 거래를 진행할 때 프로그램이 매수와 매도를 진행하기 위해 어떤 데이터들을 필요로 하는지에 대해 사전에 모두 기록해둔 후에 변수를 생성하는 것을 권장한다.(물론 이렇게 필요한 변수를 모두 생각해서 데이터프레임을 생성했다 하더라도, 실제로 거래를 진행하도록 해보면 필요한 변수들이 또 생기기 마련이므로 자책하지 말고 그건 그 때 추가해주도록 하자. 어차피 당신은 수정의 연속이라는 길을 걷게 된다.)

일단 다른 내용들은 차치하고, 이번 게시글에서는 가장 기본적인 변수라 할 수 있는 종목코드와 매수 예정 가격에 대해서만 구현해볼 예정이다.(추후에 분할 매수 기능을 구현할 것이기 때문에 매수 에정 가격은 두 개로 나누어 생성할 예정이다.) 이전에 생성해두었던 _df.py 파일을 열어 데이터를 입력할 데이터프레임을 생성해주도록 하자.
※ Line : 10~13

"""데이터프레임 변수 생성"""

import pandas as pd

def setrealreg():
    data = {'item_code':[]}
    data = pd.DataFrame(data, columns=['item_code'])
    return data

def algo1_df():
    data = {'item_code':[], 'first_buy_price':[], 'second_buy_price':[]}
    data = pd.DataFrame(data, columns=['item_code', 'first_buy_price', 'second_buy_price'])
    return data

 

이제 algorithm_1.py 파일 내에서 _df.py를 불러와서 해당 데이터프레임에 데이터를 입력해보도록 하자.
※ Line : 4, 9

"""algorithm_1.py"""
import pandas as pd
import manage_db
from valuable import _df

class algo1:

    def __init__(self, min5_data, start_date):
        self.df = _df.algo1_df()
        self.min5_data = min5_data
        self.start_date = start_date
        self.min5_data['date'] = self.min5_data['time'].str.slice(start=0, stop=8)
        data_of_start_date = self.min5_data[self.min5_data['date'] == self.start_date].reset_index(drop=True)
        self.start_price = int(data_of_start_date.loc[0, 'open'])
        self.tem_min5_data = self.min5_data[self.min5_data['date'] >= self.start_date]      ## 기준일자 이후의 5분봉 차트 데이터

        start_date_data = self.min5_data[self.min5_data['date'] == self.start_date]
        self.standard_high = max(start_date_data['high'])

 

이제   def run(self):  함수로 돌아와서   self.df  변수에 데이터를 입력해주면 된다.
※ Line : 5, 15, 30

def run(self):
    self.highest_price = self.start_price
    self.lowest_price = 0
    self.pos_buy = False
    idx_of_df = len(self.df['item_code'])

    for data in self.tem_min5_data.itertuples():
        high = int(data.high)
        low = int(data.low)

        if high > self.highest_price:
            print(f"고가가 갱신되었습니다... {data.date}, {data.time}")
            self.highest_price = high  ## 고가 데이터 재입력
            self.buy_price = (self.start_price + self.highest_price) / 2
            self.df.loc[idx_of_df, 'first_buy_price'] = self.buy_price
            self.lowest_price = high

            if high == self.standard_high:
                self.pos_buy = True

        if low <= self.lowest_price:
            print(f"저가가 갱신되었습니다... {data.date}, {data.time}")
            self.lowest_price = low

            if self.pos_buy:
                if low < self.buy_price:
                    print(f"매수 예정 가격에 닿았습니다... [{data.time}] {low}/{self.buy_price}")
                    return False, '0'

    return True, self.df

가장 먼저   Line 5  에서는   idx_of_df  라는 변수 내에   self.df  라는 변수의 정확히 어떤 자리(인덱스 번호)에 데이터를 입력할지를 결정할 수 있는 인덱스 번호를 구하도록 한다. 그 후에 고가 데이터가 갱신될 때마다 매수 예정 가격이 새롭게 계산되는데, 이 때 매수 예정 가격을 계산해내는   Line 15  에서 매수 예정 가격을 계산함과 동시에   self.df  변수 내에 매수 예정 가격 데이터를 입력해주는 것이다.

이제 다음 게시글에서는 여기서 생성된 데이터프레임을 어떻게 반환받을 수 있는지 그리고 어떻게 사용할 수 있는지 그 방법에 대해 살펴볼 예정이다.

 

 


728x90
반응형
Contents

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

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