주식 자동 매매 시스템: 매수 신호 확인 및 주문 처리 코드 분석
이번 블로그에서는 주식 자동 매매 시스템에서 사용되는 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
매수 조건 확인 및 주문 실행
- 조건: 매수 조건은 다음 세 가지를 만족해야 합니다:
- ma20 > ma60: 20일 이동평균이 60일 이동평균보다 커야 합니다 (상승 추세).
- RSI < 5: RSI가 5 미만이어야 합니다 (과매도 상태).
- 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, 이동평균, 가격 차이를 조합하여 매수 신호를 판단하는 데 있으며, 그 신호를 바탕으로 체계적인 예산 관리와 주문 처리를 통해 자동으로 매매가 이루어집니다.
이 시스템은 효율적인 알고리즘을 통해 조건에 맞는 종목만 매수하며, 위험을 관리하면서 수익을 극대화하려고 합니다.