카테고리 없음

[Python] 주식 자동 매매 시스템: 매수 신호 확인 및 주문 처리 코드 분석

indigenthuman 2024. 10. 6. 13:41

주식 자동 매매 시스템: 매수 신호 확인 및 주문 처리 코드 분석

이번 블로그에서는 주식 자동 매매 시스템에서 사용되는 check_buy_signal_and_order 함수를 중심으로 코드를 상세히 분석해 보겠습니다. 이 코드는 실시간 주식 데이터를 기반으로 특정 조건을 만족하면 매수 주문을 자동으로 생성하는 기능을 합니다. 각 단계마다 어떤 기능을 수행하는지 자세히 설명하겠습니다.


1. 매수 신호 확인 및 주문 처리 함수

def check_buy_signal_and_order(self, code):
    # 장 종료 시간 근처면 함수 진행, 장 종료 시간 근처가 아니면 함수 종료
    if not check_adjacent_transaction_closed_for_buying():
        return False

장 종료 시간 확인

  • 목적: 장 종료 시간에 가까워졌을 때만 매매가 가능하도록 하여, 거래 시간이 아니거나 불필요한 신호에 따른 매매를 방지하기 위함입니다.
  • check_adjacent_transaction_closed_for_buying() 함수는 장 종료 시간이 가까운지 여부를 확인합니다. 시간이 적절하지 않으면 함수를 종료(return False)합니다.

    universe_item = self.universe[code]

    if code not in self.kiwoom.universe_realtime_transaction_info.keys():
        print("매수대상 확인 과정에서 아직 체결정보가 없습니다.")
        return

종목 데이터 확인

  • 목적: code로 특정 종목을 나타내며, self.universe[code]는 해당 종목의 세부 데이터를 저장한 딕셔너리입니다. 이 데이터를 기반으로 분석을 진행합니다.
  • 실시간 체결 정보 확인: 매매를 위해 실시간 체결 정보가 있어야 합니다. 만약 실시간 체결 정보가 없다면 함수는 종료됩니다. 이는 신뢰할 수 없는 데이터를 기반으로 매매를 진행하지 않도록 하기 위함입니다.

    open = self.kiwoom.universe_realtime_transaction_info[code]['시가']
    high = self.kiwoom.universe_realtime_transaction_info[code]['고가']
    low = self.kiwoom.universe_realtime_transaction_info[code]['저가']
    close = self.kiwoom.universe_realtime_transaction_info[code]['현재가']
    volume = self.kiwoom.universe_realtime_transaction_info[code]['누적거래량']

    today_price_data = [open, high, low, close, volume]

실시간 가격 정보 추출

  • 목적: 해당 종목의 실시간 가격 데이터를 저장합니다. 주식 매매를 위해서는 실시간 주식 가격 정보가 필요하며, 여기서 '시가', '고가', '저가', '현재가', '누적 거래량' 등을 가져옵니다.
  • 이러한 데이터는 이후 매수 신호 판단과 주문 실행에 사용됩니다.

    df = universe_item['price_df'].copy()

    # 오늘날짜를 인덱스로 하는 데이터값을 추가
    df.loc[datetime.now().strftime('%Y%m%d')] = today_price_data

데이터프레임에 실시간 데이터 추가

  • 목적: universe_item['price_df']는 과거 가격 데이터를 담은 DataFrame 객체입니다. 오늘 날짜의 실시간 데이터를 추가하여 최신 가격 정보를 반영합니다.
  • datetime.now().strftime('%Y%m%d'): 오늘 날짜를 YYYYMMDD 형식으로 가져와 인덱스로 사용하고, today_price_data는 앞서 수집한 실시간 가격 정보가 포함된 리스트입니다.
    period = 2
    date_index = df.index.astype('str')

    U = np.where(df['close'].diff(1) > 0, df['close'].diff(1), 0)
    D = np.where(df['close'].diff(1) < 0, df['close'].diff(1) * (-1), 0)

    AU = pd.DataFrame(U, index=date_index).rolling(window=period).mean()
    AD = pd.DataFrame(D, index=date_index).rolling(window=period).mean()
    RSI = AU / (AD + AU) * 100
    df['RSI(2)'] = RSI

RSI 계산 (Relative Strength Index)

  • RSI 개념: RSI는 주가의 상승과 하락을 기준으로 매수, 매도 신호를 판단하는 기술적 지표입니다.
  • U와 D는 각각 주가가 상승한 날과 하락한 날의 가격 차이를 계산한 것입니다.
  • 이동평균(AU와 AD)을 구한 뒤, 이를 바탕으로 RSI를 계산하고 데이터프레임에 RSI(2)라는 새로운 열을 추가합니다. 여기서 RSI는 2일 동안의 가격 움직임을 기준으로 합니다.

    df['ma20'] = df['close'].rolling(window=20, min_periods=1).mean()
    df['ma60'] = df['close'].rolling(window=60, min_periods=1).mean()

이동 평균 (MA) 계산

  • 목적: 20일과 60일 동안의 주가 이동 평균을 계산하여, 주식의 중장기적인 추세를 파악합니다.
  • rolling(window=20)와 rolling(window=60)는 각각 20일과 60일 기간 동안의 이동평균을 계산하고, 이를 새로운 열로 추가합니다.

    rsi = df[-1:]['RSI(2)'].values[0]
    ma20 = df[-1:]['ma20'].values[0]
    ma60 = df[-1:]['ma60'].values[0]

최신 RSI 및 이동 평균 추출

  • 목적: 가장 최근 데이터에서 RSI, 20일 이동 평균, 60일 이동 평균을 추출합니다. 이 값들은 매수 신호를 결정하는데 중요한 역할을 합니다.

    idx = df.index.get_loc(datetime.now().strftime('%Y%m%d')) - 2
    close_2days_ago = df.iloc[idx]['close']
    price_diff = (close - close_2days_ago) / close_2days_ago * 100

2일 전 종가와의 가격 차이 계산

  • 목적: 2일 전 종가와 현재가의 가격 차이를 계산하여 가격이 얼마나 하락했는지 판단합니다. 매수 신호를 결정할 때 가격이 일정 비율 이상 하락했는지 확인하는 중요한 조건 중 하나입니다.

    if ma20 > ma60 and rsi < 5 and price_diff < -2:
        if (self.get_balance_count() + self.get_buy_order_count()) >= 10:
            return

        budget = self.deposit / (10 - (self.get_balance_count() + self.get_buy_order_count()))
        bid = self.kiwoom.universe_realtime_transaction_info[code]['(최우선)매도호가']
        quantity = math.floor(budget / bid)

        if quantity < 1:
            return

        amount = quantity * bid
        self.deposit = math.floor(self.deposit - amount * 1.00015)

        if self.deposit < 0:
            return

        order_result = self.kiwoom.send_order('send_buy_order', '1001', 1, code, quantity, bid, '00')
        self.kiwoom.order[code] = {'주문구분': '매수', '미체결수량': quantity}
    else:
        return

매수 조건 확인 및 주문 실행

  • 조건: 매수 조건은 다음 세 가지를 만족해야 합니다:
    1. ma20 > ma60: 20일 이동평균이 60일 이동평균보다 커야 합니다 (상승 추세).
    2. RSI < 5: RSI가 5 미만이어야 합니다 (과매도 상태).
    3. price_diff < -2: 2일 전 가격 대비 2% 이상 하락해야 합니다.
  • 주문 실행: 매수 조건을 만족하면, self.get_balance_count()와 self.get_buy_order_count()를 합한 값이 10개 이상일 경우 매수를 하지 않고, 그렇지 않다면 매수 가능한 예산을 계산하여 주문을 생성합니다.

2. 보유 종목 수 계산 함수

def get_balance_count(self):
    balance_count = len(self.kiwoom.balance)
    for code in self.kiwoom.order.keys():
        if code in self.kiwoom.balance and self.kiwoom.order[code]['주문구분'] == "매도" and self.kiwoom.order[code]['미체결수량'] == 0:
            balance_count -= 1
    return balance_count
  • 목적: 현재 보유 중인 종목 수를 계산합니다. 만약 매도 주문이 체결된 종목은 보유하지 않은 것으로 간주하여 보유 종목 수에서 제외합니다.

3. 미체결 매수 주문 수 계산 함수

def get_buy_order_count(self):
    buy_order_count = 0
    for code in self.kiwoom.order.keys():
        if code not in self.kiwoom.balance and self.kiwoom.order[code]['주문구분'] == "매수":
            buy_order_count += 1
    return buy_order_count
  • 목적: 현재 미체결 매수 주문의 수를 반환합니다. 종목이 아직 보유 중이지 않고 매수 주문이 체결되지 않은 상태인 경우를 포함합니다.

결론

이 코드는 주식의 실시간 데이터를 바탕으로 매수 신호를 분석하고, 특정 조건을 만족할 때 자동으로 매수 주문을 생성하는 주식 매매 시스템입니다. 핵심은 RSI, 이동평균, 가격 차이를 조합하여 매수 신호를 판단하는 데 있으며, 그 신호를 바탕으로 체계적인 예산 관리와 주문 처리를 통해 자동으로 매매가 이루어집니다.

이 시스템은 효율적인 알고리즘을 통해 조건에 맞는 종목만 매수하며, 위험을 관리하면서 수익을 극대화하려고 합니다.