본문 바로가기
프로그램

[파이썬] 주식 매수/매도 테스트 (백테스트)

by 오디세이99 2022. 8. 11.
728x90
반응형

주식 트레이딩(백테스트)하는 코드 입니다.

이를 할 수 있는 여러가는 파이썬 package가 있지만 전체를 이해하기 위해 직접 코드를 만들어 봅니다.

 

트레이딩 기본 개념은 20일선,60일선으로 매수,매도 시점을 찾고

초기 자산은 1,000,000원 입니다.

매수/매도 해당 일의 종가로 자산 전체를 투자 합니다.

모든 일이 끝나면 보유한 주식을 모두 매도하여 최종 자산으로 합니다.

(주의 : 거래시 수수료, 세금은 없는 것으로 합니다)

# Stock Back Testing
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pykrx import stock

code = '069500'
name = 'KODEX200'
start = '2019-07-01'
end = '2022-04-30'

# 주식 데이터 받기
df = stock.get_market_ohlcv(start, end, code)
# 컬럼명 변경
df.columns = ['Open', 'High', 'Low', 'Close', 'Volume']

# 이동편균선 데이터 만든기
df['ma5'] = df['Close'].rolling(window=5).mean()
df['ma20'] = df['Close'].rolling(window=20).mean()
df['ma60'] = df['Close'].rolling(window=60).mean()
df['ma120'] = df['Close'].rolling(window=120).mean()

# Null 데이터 삭제
df = df.dropna()


# 골든크로스(Golden cross) 및 데드크로스(Death cross) 확인(20일, 60일 이동편균선)
# 매수, 매도 지점 등록
buy = []
sell = []
asset = []      # 자산
profit = []     # 수익율
stock_cnt = []  # 보유 주식수
def chkTrade(df):
    chk = 0
    trd_cnt = 0
    for i in range(len(df)):
        buy.append(False)
        sell.append(False)
        if i == 0:
            asset.append(1000000)    # 초기자금
            stock_cnt.append(0)      # 초기 주식 보유수
        else:
            asset.append(asset[i-1])           # 매일은 어제의 asset을 그대로 이어진다 
            stock_cnt.append(stock_cnt[i-1])   # 매일은 어제의 cnt를 그대로 이어진다
        
        if df['ma60'][i] < df['ma20'][i] and chk == 0:
            # print('Golden cross ', str(df.index[i])[:10])
            stock_cnt[i] = int(asset[i]/df['Close'][i])   # 매수 수량 = 자산/종가
            asset[i] = asset[i] - (stock_cnt[i] * df['Close'][i])   # 매수 후 자산=매수수략*종가
            chk = 1
            buy[i] = True
            trd_cnt += 1
            print('[' + str(trd_cnt) + '] 매수[' + str(df.index)[:10] + '] : 수량=' + str(stock_cnt[i]) + ' /자산=' + format(asset[i],','))
        elif df['ma60'][i] > df['ma20'][i] and chk == 1:
            # print('Death cross ', str(df.index[i])[:10])
            asset[i] = asset[i] + (stock_cnt[i] * df['Close'][i])   # 매도 후 자산=매수수략*종가
            stock_cnt[i] = 0   # 매도 후 보유 수량 = 0 (전부 매도일 경우)
            chk = 0
            sell[i] = True
            trd_cnt += 1
            print('[' + str(trd_cnt) + '] 매도[' + str(df.index)[:10] + '] : 수량=' + str(stock_cnt[i]) + ' /자산=' + format(asset[i],','))


# 골든코로스/데드코로스 함수 실행
chkTrade(df)


df['buy'] = buy
df['sell'] = sell
df['asset'] = asset
df['stock_cnt'] = stock_cnt


# 최종 처리
assetTot = df['asset'][-1]      # 최종 자산=마지막 Row의 자산
if df['stock_cnt'][-1] != 0:    # 마지막에 매도를 하지 않고 매수된 상태라면 최종 매도해서 자산을 확인
    assetTot = asset[-1] - (stock_cnt[-1] * df['Close'][-1]) 

print('\n### 최종 자산=', format(assetTot, ','))
            


%matplotlib Qt5
sub_cnt = 3
fig = plt.figure(figsize=(15, 8))
plt.subplot(sub_cnt, 1, 1)
plt.plot(df['Close'],label='Close')
plt.plot(df['ma20'],label='ma20')
plt.plot(df['ma60'],label='ma60')
plt.plot(df.ma20[df.buy == True], '^')     # 매수지점 Marking
plt.plot(df.ma20[df.sell == True], 'v')    # 메도지점 Marking

# 매도, 매수 시점에 종가 Text 보여 주기. +500은 Text가 조금 높게 보이도록 함
for i in range(len(df)):
    if df.buy[i] == True:
        # (X, Y, Text), X는 index, Y는 ma20 값에 + 500
        plt.text(df.index[i], int(df['ma20'][i]) + 500, str(df['Close'][i]))

    if df.sell[i] == True:
        plt.text(df.index[i], int(df['ma20'][i]) + 500, str(df['Close'][i]))
plt.title('Trade(asset=' + format(assetTot,',') + ')')
plt.legend()
plt.grid()

plt.subplot(sub_cnt, 1, 2)
plt.plot(df['asset'], label='자산')
plt.legend()
plt.grid()

plt.subplot(sub_cnt, 1, 3)
plt.plot(df['stock_cnt'], label='주식보유수')
plt.legend()
plt.grid()

plt.tight_layout()
plt.show()

 

여기서는 이득이 되었네요.  이렇게만 된다면 좋겠지만...

[1] 매수[DatetimeIn] : 수량=35 /자산=26,580
[2] 매도[DatetimeIn] : 수량=0 /자산=957,755
[3] 매수[DatetimeIn] : 수량=39 /자산=10,796
[4] 매도[DatetimeIn] : 수량=0 /자산=1,564,283
[5] 매수[DatetimeIn] : 수량=37 /자산=12,059
[6] 매도[DatetimeIn] : 수량=0 /자산=1,567,465
[7] 매수[DatetimeIn] : 수량=40 /자산=19,265
[8] 매도[DatetimeIn] : 수량=0 /자산=1,460,265

### 최종 자산= 1,460,265

 

3개의 Chart를 그립니다.

1번은 종가,20일선,60일선,매수,매도를 보여 줍니다.

2번은 자산(asset)의 변화를 보여 줍니다.

3번은 주식 보유 수량을 보여 줍니다.

728x90
반응형

댓글